JDA*_*ers 59 python string-interpolation python-3.x 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()))
Run Code Online (Sandbox Code Playgroud)
现在我可以这样做,直接替换变量:
names = ["foo", "bar"]
for name in names:
print (f"The current name is {name}")
Run Code Online (Sandbox Code Playgroud)
但是,有时在其他地方定义模板是有意义的 - 在代码中更高,或从文件或其他东西导入.这意味着模板是一个带有格式标签的静态字符串.必须在字符串上发生一些事情,告诉解释器将字符串解释为新的f字符串,但我不知道是否有这样的事情.
有没有办法引入一个字符串并将其解释为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)
Run Code Online (Sandbox Code Playgroud)
...使用此期望的输出(不读取文件两次):
The current name is foo
The current name is bar
Run Code Online (Sandbox Code Playgroud)
...但我得到的实际输出是:
The current name is {name}
The current name is {name}
Run Code Online (Sandbox Code Playgroud)
Pau*_*zer 13
这是一个完整的"理想2".
它不是一个甚至不使用f字符串的f字符串.但它按要求提供.完全按照指定的语法.因为我们没有使用eval,所以没有安全问题.
它使用一个小类和实现eval(),它通过print自动调用.为了逃避类的有限范围,我们使用__str__模块向上跳一帧并查看调用者可以访问的变量.
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
Run Code Online (Sandbox Code Playgroud)
Tig*_*kT3 13
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
Run Code Online (Sandbox Code Playgroud)
不过,你已经成功地完成所有的替换str.format用eval,这肯定是不值得的.只需通过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())
Run Code Online (Sandbox Code Playgroud)
打印出:
The current name is foo
The current name is bar
Run Code Online (Sandbox Code Playgroud)
但感觉不对,并受到以下事实的限制:您只能偷看替换中的全局名称空间。在需要本地名称的情况下尝试使用它会惨遭失败,除非将其作为参数传递给字符串(这完全是关键)。
有什么方法可以引入一个字符串,并将其解释为f字符串,以避免使用该
.format(**locals())调用?
除了功能(包括限制)外,不行,所以不妨坚持.format。
或者也许不使用 f 字符串,只需格式化:
fun = "The curent name is {name}".format
names = ["foo", "bar"]
for name in names:
print(fun(name=name))
Run Code Online (Sandbox Code Playgroud)
在没有名字的版本中:
fun = "The curent name is {}".format
names = ["foo", "bar"]
for name in names:
print(fun(name))
Run Code Online (Sandbox Code Playgroud)
将字符串评估为f字符串(具有完整功能)的简洁方法是使用以下函数:
def fstr(template):
return eval(f"f'{template}'")
Run Code Online (Sandbox Code Playgroud)
然后,您可以执行以下操作:
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
Run Code Online (Sandbox Code Playgroud)
而且,与许多其他建议的解决方案相比,您还可以执行以下操作:
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
Run Code Online (Sandbox Code Playgroud)
您想要的似乎被视为 Python增强功能。
同时 - 从链接的讨论中 - 以下似乎是不需要使用的合理解决方法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)
Run Code Online (Sandbox Code Playgroud)
输出:
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)
Run Code Online (Sandbox Code Playgroud)
受到kadee的回答的启发,以下内容可用于定义 deferred-f-string 类。
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)
Run Code Online (Sandbox Code Playgroud)
这正是问题所要求的
使用.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)
Run Code Online (Sandbox Code Playgroud)
这样做的优点是能够执行以下操作: log.fdebug("{obj.dump()}")....除非启用调试,否则不转储对象。
恕我直言:这应该是f字符串的默认操作,但是现在为时已晚。F字符串评估可能会产生大量和意想不到的副作用,并且以延迟的方式发生会改变程序的执行。
为了适当延迟f字符串,python需要某种方式来显式切换行为。也许使用字母“ g”?;)
怎么样:
s = 'Hi, {foo}!'
s
> 'Hi, {foo}!'
s.format(foo='Bar')
> 'Hi, Bar!'
Run Code Online (Sandbox Code Playgroud)
小智 5
这些答案中的大多数有时会给你一些行为类似于 f 字符串的东西,但在某些情况下它们都会出错。pypi 上有一个包f-yeah可以完成所有这些工作,只需要花费你两个额外的字符!(完全公开,我是作者)
from fyeah import f
print(f("""'{'"all" the quotes'}'"""))
Run Code Online (Sandbox Code Playgroud)
f 字符串和格式调用之间有很多差异,这里是一个可能不完整的列表
f"The argument is {spam=}"使用 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"))
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
在某些情况下,此示例还会出现变量作用域错误。
| 归档时间: |
|
| 查看次数: |
7256 次 |
| 最近记录: |