是否有可调用的等效于f-string语法?

时间:2017-10-10 19:28:12

标签: python string templates

每个人都喜欢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字符串很精彩,我很喜欢它们。但它们有一个障碍:它们会立即进行评估,而使用str.format()我可以将其格式化为模板保存,并在不同的上下文中多次格式化。

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

更新

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

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

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

2 个答案:

答案 0 :(得分:2)

简要回答:没有。

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

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-string,但将tpl转换为自己的 repr 以立即进行评估。通常,对于简单类型,eval(repr(val))应返回val。但我们不是只放repr(tpl)(或{tpl!r}),而是将常规字符串的 repr 转换为f字符串,然后对其进行评估。

答案 1 :(得分:1)

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

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

eval(fize(...))

其工作原理如下:

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

如前所述,尝试定义例如:

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

通常不能很好地工作,例如:

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


# this does NOT work
def func(a, b, text='{a}, {b}'):
    return fmt(text)


func(1, 2)
# NameError: name 'a' is not defined

在以下情况下有效:

def func(a, b, text='{a}, {b}'):
    return eval(fize(text))


func(1, 2)
# '1, 2'

EDIT :关于fmt()函数的更新)。