使用formatted string literal时,可以在某种程度上嵌套 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'
?如果没有,阻止它的第一个和第二个字符串之间有什么区别?
答案 0 :(得分:3)
有一些库已经开发出用于评估数值和逻辑表达式安全(“安全”是关键字)的函数。
首先,设置 -
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的安全评估类似于ne.evaluate
。
>>> import pandas as pd
>>> pd.eval(f'{a} {op} {c}')
12
答案 1 :(得分:2)
我认为,当调用每个表达式时,查看实际发生的情况会很有帮助。
f"{f'{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 也找到一个它评估的表达式,这导致对{的调用{1}}
BINARY_MULTIPLY
f"{f'{expr}'}"
这里,第一个 f-string 遇到一个表达式并对其进行求值,而内部的 f-string 遇到一个字符串,导致调用{{1而不是将字符串的内容作为Python代码进行评估。
同样重要的是要在第二个示例中注意到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
和LOAD_FAST
对LOAD_FAST
和a
的调用,这些调用都出现在第一个示例中。
答案 2 :(得分:2)
它被称为"字符串文字插值"。字符串必须是文字,即在编译发生时,编译器会将字符串转换为正确的可执行代码。如果你已经有一个字符串作为一个值(而不是文字),那就太晚了。
我无法访问启用了PEP 498的Python,因此我的示例将使用Ruby,这种机制已经有很长时间了。 Python f"...{expr}..."
的Ruby语法是"...#{expr}..."
。
在Ruby中,"a#{2 * 3}b"
是["a", (2 * 3), "b"].join
的语法糖(因为它们产生完全相同的字节码)。如果您已将字符串"2 * 3"
作为值,则编译器无法对其执行任何操作;将字符串值转换为结果的唯一方法是对其进行评估。
在第一个示例中,您在字符串文字中有一个字符串文字;两者都是在编译时由编译器处理的:当编译器看到外部文字时,它会编译它,在那里找到另一个字符串文字,同时编译它,生成代码。事实上,"a#{"#{2 * 3}"}b"
再次生成完全相同的字节代码。
这是在编译时完成的事实也是字符串文字插值在内部表达式格式错误时会引发语法错误的原因,即使有问题的行永远不会执行:if false; "#{1+}"; end
将生成SyntaxError
。
这是在编译时完成的事实意味着变量中的字符串不符合此机制的条件。在您的代码中,在评估res
时,expr
可能是任何东西;唯一的出路是evil
(或另一个更安全的评估员)。