在f-string中,星号(*)的作用是什么?

27
在Python文档2.4.3.格式化字符串字面量中,似乎可以在f-string的{}中编写一个星号后跟一个表达式,但我找不到如何使用它的方法。
这是什么意思,我该如何使用它?是否有相关文档?
确切地说,这涉及以下BNF"*" or_expr部分。
f_string          ::=  (literal_char | "{{" | "}}" | replacement_field)*
replacement_field ::=  "{" f_expression ["!" conversion] [":" format_spec] "}"
f_expression      ::=  (conditional_expression | "*" or_expr)
                         ("," conditional_expression | "," "*" or_expr)* [","]
                       | yield_expression

我在 REPL 中尝试了它,但是会导致错误。

>>> l = [1, 2, 3]
>>> f"{l}"
'[1, 2, 3]'
>>> f"{*l}"
  File "<stdin>", line 1
SyntaxError: can't use starred expression here

3
请看这里:https://realpython.com/python-f-strings/。他们提供了一个很好的列表,介绍了Python支持的新的字符串格式化方式。说实话,我从未在我生命中见过这种类型的星号。也许这是一些新东西。希望能有所帮助。 - Gaurav Mall
3个回答

17

对于f_expression,有两个选择:逗号分隔的or_expr列表,可选地带有星号前缀,或单个的yield_expression。请注意,yield_expression不允许出现星号。

我认为意图是只有在至少有一个逗号的情况下才选择逗号分隔的列表作为备选项,但语法实际上并没有说那样。我觉得末尾的重复运算符应该是+而不是*

因此,f"{*1}"将是语法错误,因为有星号,但没有逗号。 f"{*1,*2}" 在语法上是有效的,但是类型错误,因为1和2不可迭代。 f"{*[1], *[2]}"是有效的,并且与f"{1,2}"相同。所以星号是被允许的,因为它在元组中充当展开运算符,可以在f表达式中不使用括号写成。

请注意,将or_expr用作*的操作数并不意味着必须在那里使用位或运算符-它只是表示位或运算符是优先级层次结构中允许用作*操作数的第一个运算符。因此,这只涉及将前缀*的优先级与其他表达式区分开来。我相信在语法中到处都使用or_expr作为前缀*的操作数(也就是说,在前缀*后跟表达式而不是参数名称的任何地方)。

1
@Kasrâmvd “是的,但它也允许(基于建议语法)非逗号分隔的表达式。” 它允许非逗号分隔的表达式(通过yield_expression替代方案),但不允许其中包含星号。只有在至少有一个逗号的情况下才允许使用星号。 - sepp2k
1
这似乎回答了我的问题。我测试并确认 f"{*l, *l}"f"{*l,}" 是有效的 f-string,其中 l = [1,2,3]f_expression 的定义不完美,但可能旨在保持简单。 - Takahiro Kobayashi
1
解释 or_expr 用于修复优先级层次结构中的 * 可以并入答案的注释。这是混淆的主要来源(以及 f"{*<...>}" 是否有意义,答案已经涵盖)。 - user4815162342
@sepp2k 谢谢你的帮助:我无法理解那个定义。对我来说,它的定义是一个 or_expr 是一个 xor_expr 或者一个 or_expr 或者一个 xor_expr。这不可能是正确的阅读方式。我该如何学习如何阅读它?从 or_expr 的定义到“按位或运算符是优先级层次结构中允许作为 * 操作数的第一个运算符”这一步是如何实现的?这是否可以从 BNF 中清楚地表达出来,还是惯例? - Princess Ruthie
因此,由于在语法中“or_expr”被用作“”的操作数,并且“or_expr”只能匹配那些东西,所以根据语法,只有这些运算符可以作为“”的操作数(不使用括号)。 - sepp2k
显示剩余7条评论

0
刚刚偶然看到这个问题,觉得可能是因为与其他相关陈述的对称性有关,例如:
a = 1,
a = (1,)
a = 1,*()
a = *[1],

所有的结果都会导致将`a`赋值给一个包含`1`的元组。然而,请注意,不要忘记在单个逗号后面加上`a`,例如:
return *[1]

相反,这是一个SyntaxError。这与f-strings的行为相似,很可能是因为Python语言与CPython实现密不可分。

查看CPython 3.11的语法规则可以发现,OP指向的规则在Grammar/python.gram中不存在。而存在的是以下规则:

fstring[expr_ty]: star_expressions

说一个只是一个,就像在其他各种地方(例如通过上面的赋值)中可以使用的那样。语言文档中出现的规则只是完整语法的一个简化版本,它更一般地定义了。
因此,我不确定在这里将Python与CPython分开有多相关,因为人们经常尝试这样做。通过对CPython解析器进行方便的更改所暗示的语义已被Python语言所规范化。

1
PEP 701讨论了f字符串的预PEG处理的合理性以及将其纳入新的PEG语法的动机,该语法允许解析生成器直接适应它们。 - chepner
@chepner有趣!我刚试了一下,似乎支持f"{*map(int,"1"),}"(注意嵌套的双引号),并且会生成"(1,)" - Sam Mason
1
在 PEG 语法之前,整个 f-string 必须由词法分析器识别,而词法分析器无法处理嵌套引号。(它使用的是正则表达式——我记得是真正的那种,不是能处理上下文无关和上下文有关语言之间的东西的那种正则表达式)。 - chepner

-1
您可以将其转换为字符串进行操作:

f"{str(*l)}"

这是通过语法的非常不同的部分进行解析,并且具有非常不同的语义。例如,str(*(1,2))f"{*(1,2),}"不是相同的。 - Sam Mason

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