我可以推迟/推迟对f字符串的评估吗?

时间:2017-02-27 23:24:02

标签: python python-3.x string-interpolation python-3.6 f-string

我正在使用模板字符串来生成一些文件,我喜欢为此目的的新f字符串的简洁性,以减少我以前的模板代码:

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a.format(**locals()))

现在我可以这样做,直接替换变量:

names = ["foo", "bar"]
for name in names:
    print (f"The current name is {name}")

但是,有时在其他地方定义模板是有意义的 - 在代码中更高,或从文件或其他东西导入。这意味着模板是一个带有格式标签的静态字符串。必须在字符串上发生一些事情,告诉解释器将字符串解释为新的字符串,但我不知道是否有这样的事情。

有没有办法引入字符串并将其解释为f字符串以避免使用.format(**locals())调用?

理想情况下,我希望能够像这样编码......(其中magic_fstring_function是我不理解的部分所在的地方):

template_a = f"The current name is {name}"
# OR [Ideal2] template_a = magic_fstring_function(open('template.txt').read())
names = ["foo", "bar"]
for name in names:
    print (template_a)

...使用此期望的输出(不读取文件两次):

The current name is foo
The current name is bar

...但我得到的实际输出是:

The current name is {name}
The current name is {name}

11 个答案:

答案 0 :(得分:12)

f-string只是创建格式化字符串的一种更简洁的方式,将.format(**names)替换为f。如果您不希望以这种方式立即评估字符串,请不要将其设为f字符串。将其保存为普通的字符串文字,然后在您想要执行插值时再调用format,就像您一直在做的那样。

当然,还有eval的替代方案。

template.txt

  

f'当前名称为{name}'

代码:

>>> template_a = open('template.txt').read()
>>> names = 'foo', 'bar'
>>> for name in names:
...     print(eval(template_a))
...
The current name is foo
The current name is bar

但是,您所做的只是将str.format替换为eval,这肯定是不值得的。只需通过format调用继续使用常规字符串。

答案 1 :(得分:11)

这是一个完整的“理想2”。

它不是f字符串,甚至不使用f字符串。但它按要求提供。完全按照指定的语法。因为我们没有使用eval,所以没有安全问题。

它使用一个小类并实现__str__,它由print自动调用。为了逃避类的有限范围,我们使用inspect模块向上跳一帧并查看调用者可以访问的变量。

import inspect

class magic_fstring_function:
    def __init__(self, payload):
        self.payload = payload
    def __str__(self):
        vars = inspect.currentframe().f_back.f_globals.copy()
        vars.update(inspect.currentframe().f_back.f_locals)
        return self.payload.format(**vars)

template = "The current name is {name}"

template_a = magic_fstring_function(template)

# use it inside a function to demonstrate it gets the scoping right
def new_scope():
    names = ["foo", "bar"]
    for name in names:
        print(template_a)

new_scope()
# The current name is foo
# The current name is bar

答案 2 :(得分:6)

  

这意味着模板是一个带有格式标签的静态字符串

是的,这正是我们为什么会有包含替换字段和.format的文字的原因,因此我们可以随时通过调用format来替换这些字段。

  

必须在字符串上发生一些事情,告诉解释器将字符串解释为新的f-string

这是前缀f/F。你可以把它包装在一个函数中,并在调用时间推迟评估,但当然会产生额外的开销:

template_a = lambda: f"The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a())

打印出来:

The current name is foo
The current name is bar

但感觉不对,并且受限于您只能查看替换中的全局命名空间这一事实。试图在需要本地名称的情况下使用它将失败,除非作为参数传递给字符串(完全胜过这一点)。

  

有没有办法引入字符串并将其解释为f字符串以避免使用.format(**locals())调用?

除了功能(包括限制),nope之外,还可以坚持使用.format

答案 3 :(得分:5)

一种将字符串评估为f字符串(具有完整功能)的简洁方法是使用以下函数:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
favorite

<div class="secim" secim="tasarim">
<div class="tasarim-icerik">
<tasarim class="secimler">TASARIM</tasarim>
<img src="img/tasarim.png">
</div>
</div>

<div class="secim" secim="oyun">
<div class="oyun-icerik">
<oyun class="secimler">OYUN</oyun>
<img src="img/oyun.png">
</div>
</div>

<div class="secim" secim="hepsi">
<div class="hepsi-icerik">
<hepsi class="secimler">HEPSİ</hepsi>
<img src="img/hepsi.png">
</div>
</div>

那么您可以做:

def fstr(template):
    return eval(f"f'{template}'")

而且,与其他许多提议的解决方案相比,您也可以执行以下操作:

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print(fstr(template_a))
# The current name is foo
# The current name is bar

答案 4 :(得分:4)

您想要的东西似乎被视为Python enhancement

同时-在链接的讨论中-以下似乎是一种合理的解决方法,不需要使用eval()

class FL:
    def __init__(self, func):
        self.func = func
    def __str__(self):
        return self.func()


template_a = FL(lambda: f"The current name, number is {name!r}, {number+1}")
names = "foo", "bar"
numbers = 40, 41
for name, number in zip(names, numbers):
    print(template_a)

输出:

The current name, number is 'foo', 41
The current name, number is 'bar', 42

答案 5 :(得分:3)

或者也许不使用f-strings,只需格式化:

fun = "The curent name is {name}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name=name))

在没有名字的版本中:

fun = "The curent name is {}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name))

答案 6 :(得分:2)

怎么样:

s = 'Hi, {foo}!'

s
> 'Hi, {foo}!'

s.format(foo='Bar')
> 'Hi, Bar!'

答案 7 :(得分:1)

使用.format不是这个问题的正确答案。 Python f字符串与str.format()模板非常不同......它们可能包含代码或其他昂贵的操作 - 因此需要延迟。

这是一个延迟记录器的示例。这使用logging.getLogger的正常前导码,但是只有在日志级别正确时才添加解释f字符串的新函数。

log = logging.getLogger(__name__)

def __deferred_flog(log, fstr, level, *args):
    if log.isEnabledFor(level):
        import inspect
        frame = inspect.currentframe().f_back.f_back
        try:
            fstr = 'f"' + fstr + '"'
            log.log(level, eval(fstr, frame.f_globals, frame.f_locals))
        finally:
            del frame
log.fdebug = lambda fstr, *args: __deferred_flog(log, fstr, logging.DEBUG, *args)
log.finfo = lambda fstr, *args: __deferred_flog(log, fstr, logging.INFO, *args)

这样做的好处是能够执行以下操作:log.fdebug("{obj.dump()") ....而不会转储对象,除非启用调试。

答案 8 :(得分:1)

这些答案中的大多数都会让您在某些时候表现得有点像 f-strings,但在某些情况下它们都会出错。 pypi f-yeah 上有一个包可以完成所有这些,只需要你多花两个字符! (完全公开,我是作者)

from fyeah import f

print(f("""'{'"all" the quotes'}'"""))

f-strings 和 format 调用之间有很多区别,这里是一个可能不完整的列表

  • f-strings 允许对 python 代码进行任意评估
  • f-strings 不能在表达式中包含反斜杠(因为格式化的字符串没有表达式,所以我想你可以说这没有区别,但它确实与原始 eval() 可以做的不同)
  • 不能引用格式化字符串中的 dict 查找。可以引用 f-strings 中的 dict 查找,因此也可以查找非字符串键
  • f 字符串具有 format() 没有的调试格式:f"The argument is {spam=}"
  • f-string 表达式不能为空

使用 eval 的建议将为您提供完整的 f 字符串格式支持,但它们不适用于所有字符串类型。

def f_template(the_string):
    return eval(f"f'{the_string}'")

print(f_template('some "quoted" string'))
print(f_template("some 'quoted' string"))
some "quoted" string
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f_template
  File "<string>", line 1
    f'some 'quoted' string'
            ^
SyntaxError: invalid syntax

这个例子在某些情况下也会导致变量范围错误。

答案 9 :(得分:0)

使用f字符串的建议。你的评价 模板发生的逻辑级别并将其作为生成器传递。 您可以使用f-strings

在您选择的任何位置展开它
In [46]: names = (i for i in ('The CIO, Reed', 'The homeless guy, Arnot', 'The security guard Spencer'))

In [47]: po = (f'Strangely, {next(names)} has a nice {i}' for i in (" nice house", " fast car", " big boat"))

In [48]: while True:  
...:     try:  
...:         print(next(po))  
...:     except StopIteration:  
...:         break  
...:       
Strangely, The CIO, Reed has a nice  nice house  
Strangely, The homeless guy, Arnot has a nice  fast car  
Strangely, The security guard Spencer has a nice  big boat  

答案 10 :(得分:0)

answer by kadee的启发,以下内容可用于定义deferred-f-string类。

class FStr:
    def __init__(self, s):
        self._s = s
    def __str__(self):
        return eval(f"f'{self._s}'")
    def __repr__(self):
        return self.__str__()

...

template_a = FStr('The current name is {name}')

names = ["foo", "bar"]
for name in names:
    print (template_a)

这正是问题的要求