Python 如何推迟/推迟f字符串的计算?
我正在使用模板字符串生成一些文件,为此,我喜欢新的f字符串的简洁性,因为这样可以减少我以前的模板代码:Python 如何推迟/推迟f字符串的计算?,python,python-3.x,string-interpolation,python-3.6,f-string,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
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字符串,但我不知道是否存在这种情况
有没有办法引入字符串并将其解释为f字符串,以避免使用.format(**locals())
调用?
理想情况下,我希望能够这样编码。。。(其中,magic\u fstring\u函数
是我不理解的部分出现的地方):
…使用此所需输出(不读取文件两次):
当前名称为foo
当前名称为bar
…但我得到的实际输出是:
当前名称为{name}
当前名称为{name}
这里有一个完整的“理想2”
它不是f-string,它甚至不使用f-string,但它是按要求使用的。语法完全符合规定。没有安全问题,因为我们没有使用eval()
它使用了一个小类并实现了由print自动调用的\uuu str\uu
。为了避开类的有限范围,我们使用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
f字符串只是创建格式化字符串的一种更简洁的方法,它将
.format(**names)
替换为f
。如果不希望字符串立即以这种方式计算,请不要将其设置为f字符串。将其保存为普通字符串文字,然后在以后需要执行插值时调用format
,就像您一直在做的那样
当然,eval
还有另一种选择
template.txt
:
f'当前名称为{name}'
代码:
但是你所做的就是用eval
替换str.format
,这肯定不值得。只要在调用格式时继续使用常规字符串即可
这意味着模板是一个静态字符串,其中包含格式化标记
是的,这正是为什么我们用替换字段和.format
替换文本的原因,因此我们可以随时通过调用format
替换字段
字符串必须发生某种情况才能告诉解释器将该字符串解释为新的f字符串
这是前缀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())
调用
除了函数(包括限制),没有,所以最好还是使用.format
或者不使用f字符串,只使用格式:
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))
使用.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()}”)
。。。。除非启用调试,否则不转储对象
IMHO:这应该是f-strings的默认操作,但是现在已经太晚了。F-string评估可能会产生大量意外的副作用,以延迟的方式发生会改变程序的执行
为了使f字符串正确延迟,python需要某种方式来显式切换行为。也许用字母“g”?;)
有人指出,如果字符串转换器中存在错误,延迟日志记录不应该崩溃。上述解决方案也可以做到这一点,将finally:
更改为except:
,并在其中粘贴日志。exception
。使用f字符串的建议。你的评估是什么
发生模板化的逻辑级别,并将其作为生成器传递。
您可以使用f字符串在您选择的任何点将其展开
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
将字符串作为f字符串(具有其全部功能)进行求值的简明方法是使用以下函数:
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
而且,与许多其他建议的解决方案相比,您还可以:
template_b = "The current name is {name.upper() * 2}"
for name in names:
print(fstr(template_b))
# The current name is FOOFOO
# The current name is BARBAR
受的启发,以下内容可用于定义延迟f字符串类
class FStr:
def __init__(self, s):
self._s = s
def __repr__(self):
return eval(f"f'{self._s}'")
...
template_a = FStr('The current name is {name}')
names = ["foo", "bar"]
for name in names:
print (template_a)
这正是问题所要求的您想要什么似乎被认为是Python
同时,从链接的讨论来看,以下内容似乎是一个合理的解决方案,不需要使用eval()
:
输出:
当前名称、编号为'foo',41
当前名称、编号为'bar',42
那么:
s='Hi,{foo}!'
s
>“嗨,{foo}!”
s、 格式(foo='Bar')
>“嗨,酒吧!”
您不能使用f
字符串执行此操作。f
字符串不是数据,当然也不是字符串;这是密码。(如果需要,请使用dis
模块进行检查。)
template_b = "The current name is {name.upper() * 2}"
for name in names:
print(fstr(template_b))
# The current name is FOOFOO
# The current name is BARBAR
class FStr:
def __init__(self, s):
self._s = s
def __repr__(self):
return eval(f"f'{self._s}'")
...
template_a = FStr('The current name is {name}')
names = ["foo", "bar"]
for name in names:
print (template_a)
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)