如何使用ast.literal_eval评估f字符串

4
尝试使用 ast.literal_eval 评估f-strings时,出现了“节点或字符串格式错误”的 ValueError 错误提示:
from ast import literal_eval

a = 10

literal_eval("f'test {a}'")

抛出以下错误:
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<...> in <module>()
      3 a = 10
      4 
----> 5 literal_eval("f'test {a}'")

C:\...\lib\ast.py in literal_eval(node_or_string)
     83                     return left - right
     84         raise ValueError('malformed node or string: ' + repr(node))
---> 85     return _convert(node_or_string)
     86 
     87 

C:\...\lib\ast.py in _convert(node)
     82                 else:
     83                     return left - right
---> 84         raise ValueError('malformed node or string: ' + repr(node))
     85     return _convert(node_or_string)
     86 

ValueError: malformed node or string: <_ast.JoinedStr object at 0x000001F20CE718D0>

然而,对于原始或二进制字符串,它可以正常工作,没有问题:

>>> literal_eval("r'This'")
'This'
>>> literal_eval("b'This'")
b'This'

我能否让ast.literal_eval处理f-strings?如果可以,需要更改什么?


f'test {a}'不是字面值,它执行了一次计算(使用FORMAT_VALUE 操作码)。 - TigerhawkT3
2个回答

5
这是不可能的。f-string 类似于以下内容1
map = {}
map.update(globals())
map.update(locals())
string.format(**map)

实际上,这只是一个夸张的低估 -- f-strings 还支持其他类型的表达式,不仅仅是名称查找或像使用原始格式字符串时可以获得的简单的项目访问。例如,它们支持任何其他有效的 Python 表达式,包括函数调用、数学方程等。

>>> expr = '"boom"'
>>> f'foo{eval(expr)}'
'fooboom'

f-string使用__format__协议进行评估,并使用普通Python对其中包含的表达式进行评估。这意味着f-string不能是文字字面量,而是一个表达式1。请注意,与任何任意表达式一样,它不能安全地被评估,因此您可能不希望它通过ast.literal_eval进行“eval-able”评估。 1在AST中,可以通过JoinedStrStrFormattedValue AST节点来实现。其中唯一可能被视为文字字面量的是Str

你的代码涵盖了f字符串的常见用法(名称插值),足以解释M的失败。但它不足以允许出现在大括号或{和:之间的复杂表达式。实际等效于process(string).format(<expression0>, <expression1>, ...),其中process提取表达式,以便可以在简化的字符串外部进行评估。 - Terry Jan Reedy
@TerryJanReedy -- 是的。我曾经有一个脚注,试图解释f-strings不仅仅是名称查找的语法糖。我认为我没有表达得足够清楚,所以我尝试重新修改了一下文本,希望能更加清晰地表达。 - mgilson
所以,我们可以安全地忽略bandit的抱怨吗?参考链接:https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b307-eval - Mausy5043

0

F字符串可以包含任意复杂的表达式。一个简单的例子。

>>> a = 2; b=3; print(f'x{a**b}x')

x8x

它们既不是字面值,也不像字面值,因此不适合进行literal_eval。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接