Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何推迟/推迟f字符串的计算?_Python_Python 3.x_String Interpolation_Python 3.6_F String - Fatal编程技术网

Python 如何推迟/推迟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

我正在使用模板字符串生成一些文件,为此,我喜欢新的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)