嵌套的 f 弦

Nested f-strings

感谢David Beazley's tweet, I've recently found out that the new Python 3.6 f-strings也可以嵌套:

>>> price = 478.23
>>> f"{f'${price:0.2f}':*>20s}"
'*************8.23'

或者:

>>> x = 42
>>> f'''-{f"""*{f"+{f'.{x}.'}+"}*"""}-'''
'-*+.42.+*-'

虽然我很惊讶这是可能的,但我不知道它有多实用,什么时候嵌套 f 弦才有用?这可以涵盖哪些用例?

注意:PEP 本身并没有提到嵌套 f 弦,但是有一个 specific test case.

您可以将其用于动态。例如,假设您有一个变量设置为某个函数的名称:

func = 'my_func'

那么你可以这样写:

f"{f'{func}'()}" 

相当于:

'{}'.format(locals()[func]()) 

或者,等价地:

'{}'.format(my_func())

我不认为允许嵌套的格式化字符串文字(通过嵌套,我认为它的意思是 f'{f".."}')是仔细考虑可能的用例的结果,我更相信它只是被允许为了让他们符合他们的规范。

规范声明它们 support full Python expressions* inside brackets. It's also stated that a formatted string literal is really just an expression that is evaluated at run-time (See here, and here)。因此,只有允许格式化字符串文字作为另一个格式化字符串文字中的表达式才有意义,禁止它会否定对 Python 表达式的完全支持。

您找不到文档中提到的用例(只能在测试套件中找到测试用例)的事实是因为这可能是实现的一个很好的(副作用)效果,而不是激励使用-案例.


实际上,有两个例外:不允许空表达式,lambda 表达式必须用显式括号括起来。

我想这是为了在同一行中传递格式化参数,从而简化 f-strings 的使用。

例如:

>>> import decimal
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"
'result:      12.35'

当然,它允许程序员编写绝对不可读的代码,但这不是目的:)

我实际上刚刚遇到了类似的事情(我认为)并且我想分享一下。

我的具体情况是一个很大的脏 sql 语句,我需要有条件地使用一些非常不同的值,但一些 fstrings 是相同的(并且也在其他地方使用)。

这是我的意思的快速示例。无论如何,我选择的列都是相同的(并且也在其他地方的其他查询中使用过)但是 table 名称取决于组并且不是这样我可以循环进行。

当我有多个这样的参数时,每次都必须在 str2 中包含 mycols=mycols 感觉有点脏。

我不确定这会奏效,但很高兴它奏效了。至于它是如何 pythonic 我不太确定 tbh。

mycols='col_a,col_b'

str1 = "select {mycols} from {mytable} where group='{mygroup}'".format(mycols=mycols,mytable='{mytable}',mygroup='{mygroup}')

group = 'group_b'

if group == 'group_a':
    str2 = str1.format(mytable='tbl1',mygroup=group)
elif group == 'group_b':
    str2 = str1.format(mytable='a_very_different_table_name',mygroup=group)

print(str2)

在做一个宠物项目时,我因为编写自己的数据库库而分心了。我发现的一件事是:

>>> x = dict(a = 1, b = 2, d = 3)
>>> z = f"""
    UPDATE TABLE 
        bar 
    SET 
        {", ".join([ f'{k} = ?'     for k in x.keys() ])} """.strip()
>>> z
'UPDATE TABLE 
    bar 
SET 
    a = ?, b = ?, d = ?  '

我也对此感到惊讶,老实说,我不确定我是否会在生产代码中做这样的事情,但我也说过我不会在生产代码中做很多其他事情。

任何基本用例都需要一个字符串来完整描述要放入 f 字符串大括号内的对象 {}。例如,您需要字符串来索引字典。

所以,我最终在 ML 项目中使用它,代码如下:

scores = dict()
scores[f'{task}_accuracy'] = 100. * n_valid / n_total
print(f'{task}_accuracy: {scores[f"{task}_accuracy"]}')

我发现嵌套在做三元组时很有用。您对可读性的看法会有所不同,但我发现这一行非常有用。

logger.info(f"No program name in subgroups file. Using {f'{prg_num} {prg_orig_date}' if not prg_name else prg_name}")

因此,我的嵌套测试是:

  • 价值是否被重用? (用于表达式重用的变量)
  • 表达清楚了吗? (不超过复杂度)

在F字符串中,左括号和右括号是保留键字符。 要使用 f-string 构建 json 字符串,您必须转义括号字符。 在你的情况下只有外括号。

f"\{f'${price:0.2f}':*>20s\}"

一个何时有用的简单示例,以及一个实施示例:有时格式也是一个变量。

num = 3.1415
fmt = ".2f"
print(f"number is {num:{fmt}}")

嵌套的 f 字符串与格式说明符中的计算表达式

这个问题是关于在“外部”f 字符串的某些评估表达式中使用 f 字符串的用例。

这与允许计算表达式出现在 f 字符串 within the format specifier 中的功能不同。后一个功能非常有用,并且与这个问题有些相关,因为(1)它涉及嵌套的花括号,所以这可能是人们查看此 post 的原因,以及(2)格式说明符中允许嵌套的 f 字符串就像它们在 f 字符串的其他卷曲表达式中一样。

F 弦嵌套有助于单行

虽然肯定不是 允许嵌套 f 字符串的动机,嵌套可能在您需要或想要“单行”(例如 lambda 表达式,理解,来自终端的 python -c 命令)。例如:

print('\n'.join([f"length of {x/3:g}{'.'*(11 - len(f'{x/3:g}'))}{len(f'{x/3:g}')}" for x in range(10)]))

如果不需要单行代码,可以通过预先定义变量然后在 f 字符串的计算表达式中使用变量名称来替换任何语法嵌套(在许多情况下,如果不是大多数情况,非嵌套版本可能更具可读性和更易于维护;但是它确实需要提供变量名):

for x in range(10):
    to_show = f"{x/3:g}"
    string_length = len(to_show)
    padding = '.' * (11 - string_length)
    print(f"length of {to_show}{padding}{string_length}")

嵌套求值表达式(即在格式说明符中)很有用

与真正的 f 字符串嵌套相比,允许在 f 字符串的“格式说明符” 内评估表达式 的相关功能非常有用(正如其他人指出的那样出)有几个原因,包括:

  1. 格式可以在多个 f 字符串或计算表达式之间共享
  2. 格式可以包括计算量,可以从 运行 运行

这是一个使用嵌套计算表达式的示例,但不是嵌套 f 字符串:

import random

results = [[i, *[random.random()] * 3] for i in range(10)]
format = "2.2f"

print("category,precision,recall,f1")
for cat, precision, recall, f1 in results:
    print(f"{cat},{precision:{format}},{recall:{format}},{f1:{format}}")

但是,即使这种嵌套的使用也可以用不需要句法嵌套的更灵活(也许更简洁)的代码代替:

import random

results = [[i, *[random.random()] * 3] for i in range(10)]
def format(x):
    return f"{x:2.2f}"

print("category,precision,recall,f1")
for cat, precision, recall, f1 in results:
    print(f"{cat},{format(precision)},{format(recall)},{format(f1)}")

以下嵌套的 f 字符串单行代码在构造命令参数字符串方面做得很好

cmd_args = f"""{' '.join([f'--{key} {value}' for key, value in kwargs.items()])}"""

输入的地方 {'a': 10, 'b': 20, 'c': 30, ....}

优雅地转换为 --a 10 --b 20 --c 30 ... `

如果需要一些花哨的格式,这样的嵌套也许会有用。

for n in range(10, 1000, 100):
    print(f"{f'n = {n:<3}':<15}| {f'|{n:>5}**2 = {n**2:<7_}'} |")

我用它来格式化货币。给定的值如下:

a=1.23
b=45.67

使用前导 $ 和小数位对齐来格式化它们。例如

 .23
.67

用单个f-string格式化f"${value:5.2f}"你可以得到:

$ 1.23
.67

这有时很好,但并非总是如此。嵌套 f-strings f"${f'${value:.2f}':>6}" 给你确切的格式:

 .23
.67