嵌套 f 字符串的用例有哪些[关闭]
- 2025-04-16 08:56:00
- admin 原创
- 19
问题描述:
感谢David Beazley 的推文,我最近发现新的Python 3.6 f 字符串也可以嵌套:
>>> price = 478.23
>>> f"{f'${price:0.2f}':*>20s}"
'*************$478.23'
或者:
>>> x = 42
>>> f'''-{f"""*{f"+{f'.{x}.'}+"}*"""}-'''
'-*+.42.+*-'
虽然我很惊讶这是可能的,但我不明白这有多实用,嵌套 f 字符串什么时候有用?它能涵盖哪些用例?
注意:PEP 本身没有提到嵌套 f 字符串,但有一个特定的测试用例。
解决方案 1:
我不认为格式化的字符串文字允许嵌套(通过嵌套,我认为它的意思是f'{f".."}'
)是仔细考虑可能的用例的结果;我更确信这只是为了使它们符合其规范而允许的。
规范声明它们支持括号内的完整 Python表达式。规范还指出,格式化字符串字面量实际上只是一个*在运行时求值的表达式(参见此处和此处)。因此,允许一个格式化字符串字面量作为另一个格式化字符串字面量中的表达式才是合理的;禁止这样做将否定对 Python 表达式的完全支持。
事实上,您无法找到文档中提到的用例(并且只能在测试套件中找到测试用例),因为这可能是实现的一个很好的(副作用),而不是它的激励用例。
*实际上,有三个例外:
不允许出现空表达式。
Lambda 表达式必须被明确的括号括起来。
使用相同的引号嵌套字符串会导致语法错误。
解决方案 2:
我猜这是为了在同一行传递格式化参数,从而简化f 字符串的使用。
例如:
>>> import decimal
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"
'result: 12.35'
当然,它允许程序员编写绝对不可读的代码,但这不是目的
解决方案 3:
事实上,我刚刚遇到过类似的事情(我认为)并想分享一下。
我的具体情况是一个很大的脏 SQL 语句,其中我需要有条件地使用一些非常不同的值,但一些 fstring 是相同的(并且也在其他地方使用)。
以下是我所指的简单示例。无论如何,我选择的列都是相同的(并且也用于其他查询中),但是表名取决于组,因此我无法在循环中执行此操作。
当我有多个这样的参数时,每次都必须将其包含mycols=mycols
在 str2 中,这感觉有点麻烦。
我之前不确定这是否可行,但很高兴它成功了。至于它有多 Pythonic,说实话我不太确定。
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)
解决方案 4:
任何基本用例都是需要一个字符串来完整描述要放入 f 字符串括号内的对象{}
。例如,你需要字符串来索引字典。
因此,我最终在 ML 项目中使用了它,代码如下:
scores = dict()
scores[f'{task}_accuracy'] = 100. * n_valid / n_total
print(f'{task}_accuracy: {scores[f"{task}_accuracy"]}')
解决方案 5:
我用它来格式化货币。给定如下值:
a=1.23
b=45.67
使用 $ 开头的格式,并将小数点对齐。例如
$1.23
$45.67
使用单个 f 字符串进行格式化,f"${value:5.2f}"
您可以获得:
$ 1.23
$45.67
有时候这样没问题,但并不总是如此。嵌套的 f 字符串f"{f'${value:.2f}':>6}"
会提供精确的格式:
$1.23
$45.67
当您想在数字中添加其他单位符号时,它同样有用,例如#
。
解决方案 6:
我发现嵌套在处理三元运算符时很有用。关于可读性,大家的看法可能有所不同,但我发现这行代码非常有用。
logger.info(f"No program name in subgroups file. Using {f'{prg_num} {prg_orig_date}' if not prg_name else prg_name}")
因此,我对嵌套的测试将是:
该值是否被重用?(表达式重用的变量)
表达清楚吗?(不超过复杂度)
解决方案 7:
在 Python 3.12 之前,嵌套 f 字符串受限于字符串解析的限制。例如,f'{f""}'
是有效的,而f'{f"{f''}"}'
会导致SyntaxError
。因此,f 字符串最多只能嵌套四次(使用'
,"
以及它们各自的三重引号)。
由于 f 字符串不遵循 Python 的常规语法,这看起来有些武断、不一致且违反直觉。因此,随着 Python 3.12 的推出,此类限制已被取消,从而使示例表达式变得有效。此更改的其他动机在PEP 701f'{f"{f''}"}'
中有进一步概述。
解决方案 8:
嵌套的 f 字符串与格式说明符中的求值表达式
这个问题是关于在“外部”f 字符串的某些评估表达式中使用 f 字符串的用例。
这与允许求值表达式出现在 f 字符串的格式说明符内的功能不同。后者非常有用,并且与这个问题有一定关联,因为 (1) 它涉及嵌套的花括号,所以这可能是人们阅读这篇文章的原因;(2) 格式说明符内允许嵌套的 f 字符串,就像它们在 f 字符串的其他花括号表达式中一样。
F 字符串嵌套有助于单行代码
虽然嵌套 f 字符串并非出于允许使用嵌套 f 字符串的动机,但在一些不太常见的情况下,例如需要或想要“一行代码”(例如 lambda 表达式、推导式、python -c
终端命令),嵌套可能会有所帮助。例如:
print('
'.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 字符串的“格式说明符”内评估表达式的相关功能非常有用(正如其他人指出的那样),原因如下:
格式可以在多个 f 字符串或求值表达式之间共享
格式可以包括每次运行都可能变化的计算量
下面是一个使用嵌套求值表达式但不使用嵌套 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)}")
解决方案 9:
我在做一个自己喜欢的项目时,因为编写自己的数据库库而分心了。我发现了以下事情:
>>> 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 = ? '
我对此也感到惊讶,老实说,我不确定我是否会在生产代码中做这样的事情,但我也说过我不会在生产代码中做很多其他事情。
解决方案 10:
在 F 字符串中,左括号和右括号是保留键字符。要使用 F 字符串构建 json 字符串,必须转义括号字符。在本例中,只需要转义最外层的括号即可。
f"{f'${price:0.2f}':*>20s}"
解决方案 11:
以下嵌套的 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 ...
`
解决方案 12:
Selenium 测试验证网页上的文本是否根据动态数据输入以特定方式读取。
weeks: Literal[1,2,3,4,5,6] | None
rotation = f"Rotation:
{f'{weeks} weeks' if weeks else 'Add rotation'}"
解决方案 13:
我曾在这样的单行情况下使用过它:
s = 2.1
print(f"{f'Is {s} an int? {"True" if isinstance(s, int) else "False"}':>25}")
# Instead of having to combine with other string manipulating methods:
print(f"Is {s} an int? {"True" if isinstance(s, int) else "False"}".rjust(25))
解决方案 14:
我发现这个问题是关于如何使用空格填充而不是零填充的十进制数字(参见https://strftime.org/)来格式化月份和日期。
dt = datetime.strptime('2024 1 3 1501', '%Y %m%d %H%M')
# simple formatting with month and day as zero-padded decimal numbers
print(f"{dt:%Y %m %d %H%M}")
# 2024 01 03 1501
# no padding
print(f"{dt:%Y %-m %-d %H%M}")
# 2024 1 3 1501
# left padding with space (space-padded but aligns with 0-padded)
print(f"{dt:%Y {f'{dt:%-m}':>2} {f'{dt:%-d}':>2} %H%M}")
# 2024 1 3 1501
另请参阅如何格式化日期并用空格填充?中的示例。
# format date (no padding)
print(f"{today:%d-%m-%Y}")
# 19-05-2024
# nested f-string
print(f"{f'{today:%d-%m-%Y}':>20}")
# 19-05-2024
解决方案 15:
一个简单的例子,说明它何时有用,以及一个实现的例子:有时格式也是一个变量。
num = 3.1415
fmt = ".2f"
print(f"number is {num:{fmt}}")
解决方案 16:
如果需要一些奇特的格式,这种嵌套可能会有用。
for n in range(10, 1000, 100):
print(f"{f'n = {n:<3}':<15}| {f'|{n:>5}**2 = {n**2:<7_}'} |")
解决方案 17:
你可以用它来实现动态性。比如,假设你有一个变量,设置为某个函数的名称:
func = 'my_func'
然后你可以写:
f"{f'{func}'()}"
这相当于:
'{}'.format(locals()[func]())
或者,等效地:
'{}'.format(my_func())
扫码咨询,免费领取项目管理大礼包!