使用格式化字符串字面值时,可以在一定程度上嵌套f-strings。
a = 3
b = 7
res = f"{f'{a*b}'}"
print(res) # '21'
然而,如果内部表达式是包含字符串的变量,则该方法无法奏效。
a = 3
b = 7
expr = 'a*b'
res = f"{f'{expr}'}"
print(res) # 'a*b'
有没有办法使它工作,并且使第二个输出也是'21'
?如果没有,是什么区别导致第一个和第二个字符串不同?
使用格式化字符串字面值时,可以在一定程度上嵌套f-strings。
a = 3
b = 7
res = f"{f'{a*b}'}"
print(res) # '21'
然而,如果内部表达式是包含字符串的变量,则该方法无法奏效。
a = 3
b = 7
expr = 'a*b'
res = f"{f'{expr}'}"
print(res) # 'a*b'
有没有办法使它工作,并且使第二个输出也是'21'
?如果没有,是什么区别导致第一个和第二个字符串不同?
有一些库开发了函数来安全地评估数值和逻辑表达式("safe"是关键词)。
首先,进行设置 -
a = 3
b = 7
op = '*'
numexpr.evaluate
>>> import numexpr as ne
>>> ne.evaluate(f'{a} {op} {b}')
array(21, dtype=int32)
numexpr
能够优化您的表达式,在某些情况下甚至比numpy更快。使用 pip
进行安装。
pandas.eval
Pandas API 中的一个安全 eval,类似于 ne.evaluate
。
>>> import pandas as pd
>>> pd.eval(f'{a} {op} {c}')
12
eval
,没错。而且我仍然会。 - cs95pandas.eval
实际上有代码来解析它接收到的表达式,并且它对于认为有效和无效的操作类型非常严格。例如,将导入语句传递给pd.eval
是绝对不允许的。 - cs95我认为,在调用每个表达式时,了解其实际发生的情况可能会很有帮助。
f"{f'{a*b}'}"
这个表达式是嵌套的f-string,它将a与b相乘的结果作为内部f-string的表达式,并将其格式化为字符串。最终得到的字符串是a和b的乘积。
def om1(a, b):
return f"{f'{a*b}'}"
dis.dis(om1)
2 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_MULTIPLY
6 FORMAT_VALUE 0
8 FORMAT_VALUE 0
10 RETURN_VALUE
外部的f-string遇到一个需要计算的表达式,内部的f-string也发现了需要计算的表达式,这导致调用BINARY_MULTIPLY
f"{f'{expr}'}"
def om2(a, b):
expr = 'a*b'
return f"{f'{expr}'}"
dis.dis(om2)
2 0 LOAD_CONST 1 ('a*b')
2 STORE_FAST 2 (expr)
3 4 LOAD_FAST 2 (expr)
6 FORMAT_VALUE 0
8 FORMAT_VALUE 0
10 RETURN_VALUE
在这里,第一个f-string遇到了一个表达式并对其进行评估,而内部的f-string则遇到了一个字符串,导致调用LOAD_FAST
而不是将字符串内容作为Python代码进行评估。
此外,值得注意的是,在第二个示例中,缺少对第一个示例中存在的a
和b
的LOAD_FAST
调用。
f"...{expr}..."
的Ruby语法是"...#{expr}..."
。"a#{2 * 3}b"
是["a", (2 * 3), "b"].join
的语法糖(也就是说,它们产生完全相同的字节码)。如果您已经将字符串"2 * 3"
作为值,则编译器无法对其进行任何处理;将字符串值转换为结果的唯一方法是评估它。"a#{"#{2 * 3}"}b"
再次产生完全相同的字节码。if false; "#{1+}"; end
将产生SyntaxError
。res
被评估时,expr
可能已经是任何东西;唯一的出路是evil
(或另一个更安全的求值器)。
eval
,即res = f"{f'{eval(expr)}'}"
。 - Tobias Kienzler