【问题标题】:Is there a callable equivalent to f-string syntax?是否有等效于 f-string 语法的可调用函数?
【发布时间】:2017-10-10 19:28:12
【问题描述】:

每个人都喜欢 Python 3.6 的新 f 字符串:

In [33]: foo = {'blah': 'bang'}

In [34]: bar = 'blah'

In [35]: f'{foo[bar]}'
Out[35]: 'bang'

然而,虽然功能上非常相似,但它们的语义却与str.format() 不同:

In [36]: '{foo[bar]}'.format(**locals())
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-36-b7ef5aead76c> in <module>()
----> 1 '{foo[bar]}'.format(**locals())

KeyError: 'bar'

特别是str.format() handles getitem syntax very differently

In [39]: '{foo[blah]}'.format(**locals())
Out[39]: 'bang'

鉴于能够处理成熟的 python 表达式语法,f-strings 非常棒,我喜欢它们。但它们有一个问题:它们会立即被评估,而使用 str.format() 我可以将带有格式的字符串保存为模板,并在不同的上下文中多次格式化。

那么,有没有一种等效的方法可以将字符串保存为模板,并在以后使用 f-string 语义对其进行评估?除了定义一个函数? f-strings 是否有与 str.format() 等效的方法?

更新:

所以,这里以假设的界面为例:

In [40]: mystr = '{foo[bar]}'

In [41]: make_mine_fstring(mystr, foo=foo, bar=bar)
Out[41]: 'bang'

【问题讨论】:

  • 为什么除了定义一个函数之外还需要其他东西?这是一种显而易见的方法。
  • 好吧,假设我正在从文件中读取字符串。如何从中获取 f-string 语义?
  • 这与从文件中读取任意可执行代码并执行它基本相同。大多数情况下是个坏主意。
  • @kindall 我想 Guido 在编写 Python 解释器之前应该考虑到这一点。 ;)

标签: python string templates


【解决方案1】:

简要回答:否。

您可以阅读PEP-498 关于这些 f 字符串的信息。它清楚地定义了它们的目的和概念:这些字符串是就地评估的。结果是带有格式化内容的普通str。您不能将 f-strings 存储为模板,因为 f-strings 没有特殊对象。

PEP-498 的“Differences between f-string and str.format expressions”部分也提到了您的具体示例。

所以,无论你做什么,你要么使用内联就地 f 字符串,要么使用旧的 s.format() 语法,但行为不同。

如果你想从文件中读取一个 f-string 并根据 f-strings 的语法对其进行评估,你可以使用 eval:

foo = {'blah': 'bang', 'bar': 'sorry'}
bar = 'blah'

tpl = '{foo[bar]}'
print(tpl)

print(tpl.format(**locals()))  # sorry
print(eval(f'f{tpl!r}'))  # bang

请注意我们首先如何使用 f 字符串,但将 tpl 转换为它自己的 repr 以便立即进行评估。通常,对于简单类型,eval(repr(val)) 应该返回 val。但是,我们不只是输入repr(tpl)(或{tpl!r}),而是将常规字符串的 repr 转换为 f 字符串,然后对其进行求值。

【讨论】:

  • 我感觉答案会是something evilsome eval。 ;) 太完美了,谢谢。
【解决方案2】:

这是我能想到的最接近的,但它仍然使用eval(),并且它没有被函数很好地封装(因为范围问题)是:

def ffmt_bad(text):
    return eval('f' + repr(text))

类似:

# this does works
a = 10
ffmt_bad('{a}')
# 10


# this does NOT work
def foo():
    def bar(a, b, text='{a}, {b}'):
        return ffmt_bad(text)
    return bar(1, 2)


foo()
# NameError: name 'a' is not defined

编辑 1

一种可能的解决方法是:

import inspect


def ffmt(text):
    frame = inspect.currentframe()
    _vars = vars()
    _vars.update(frame.f_back.f_vars)
    return eval('f' + repr(text))

带有warning:

没有参数,vars() 就像 locals()。请注意,本地字典仅对读取有用,因为本地字典的更新会被忽略。

尽管它从 CPython 3.6 和 CPython 3.7 开始工作。

用法和以前一样,只是现在独立于调用它的位置:

a = 10
ffmt('{a}')
# 10


def foo():
    def bar(a, b, text='{a}, {b}'):
        return ffmt(text)
    return bar(1, 2)


foo()
# '1, 2'

编辑 2

另一个可能的解决方法是:

def fize(text):
    return 'f' + repr(text)

eval(fize(...))

它的工作原理是这样的:

a = 10
eval(fize('{a}² = {a ** 2}'))
# '10² = 100'


def foo():
    def bar(a, b, text='{a}, {b}'):
        return eval(fize(text))
    return bar(1, 2)

# '1, 2'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-29
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-17
    • 1970-01-01
    相关资源
    最近更新 更多