f 弦与 str.format()
f-strings vs str.format()
我在我的 Python 3.5 项目中经常使用 .format()
,但我担心它会在下一个 Python 版本中被弃用,因为 f-字符串,一种新的字符串文字。
>>> name = "Test"
>>> f"My app name is {name}."
'My app name is Test.'
格式化字符串功能是否可以完全取代旧的 .format()
?从现在开始,在所有情况下都使用新样式会更好吗?
我理解它基于“简单胜于复杂”的理念。但是,性能问题呢?它们之间有什么区别吗?或者它只是简单的外观相同的功能?
I'm afraid that it will be deprecated during the next Python versions
不要,str.format
不会(也没有理由)很快离开,引入 f
prefixed-strings 的 PEP 甚至 states in its Abstract:
This PEP does not propose to remove or deprecate any of the existing string formatting mechanisms.
格式化字符串的引入是为了解决其他格式化字符串方法的一些缺点;不要扔掉旧方法并强迫天知道有多少项目要使用 f-string,如果他们希望他们的代码适用于 Python 3.6+.
至于它们的性能,我最初怀疑它们可能更慢似乎是错误的,f-strings 似乎很容易胜过它们的 .format
对应物:
➜ cpython git:(master) ./python -m timeit -s "a = 'test'" "f'formatting a string {a}'"
500000 loops, best of 5: 628 nsec per loop
➜ cpython git:(master) ./python -m timeit "'formatting a string {a}'.format(a='test')"
100000 loops, best of 5: 2.03 usec per loop
在撰写本文时,这些是针对 CPython 存储库的主分支完成的;它们肯定会发生变化:
f-strings
,作为一项新功能,可能有可能的优化
- 对 CPython 的优化可能会使
.format
更快(例如 Speedup method calls 1.2x)
但真的,不要太担心速度,担心什么对你和其他人来说更具可读性。
在许多情况下,这将是 f-strings
,但 其中 format
更好。
为了基于 Jim 的回答并解决您的性能问题,我使用了 python 的 dis
模块来比较两个语法不同但功能相同的函数的字节码指令。
import dis
def f1():
a = "test"
return f"{a}"
def f2():
return "{a}".format(a='test')
print(dis.dis(f1))
print(dis.dis(f2))
其结果是:
11 0 LOAD_CONST 1 ('test')
2 STORE_FAST 0 (a)
12 4 LOAD_FAST 0 (a)
6 FORMAT_VALUE 0
8 RETURN_VALUE
None
15 0 LOAD_CONST 1 ('{a}')
2 LOAD_ATTR 0 (format)
4 LOAD_CONST 2 ('test')
6 LOAD_CONST 3 (('a',))
8 CALL_FUNCTION_KW 1
10 RETURN_VALUE
None
可以看出,f 字符串在没有属性或函数调用的情况下处理格式,这会造成类型检查和内存开销。根据 timeit
这导致大约 3 倍的性能提升(对于我的特定功能)
>>> timeit.timeit('f1()', 'from __main__ import f1', number=100000)
0.012325852433775708
>>> timeit.timeit('f2()', 'from __main__ import f2', number=100000)
0.036395029920726074
有一件事没有提到,这使得旧技术不可能被弃用,那就是插值仅适用于字符串文字。意思是,字符串在运行时呈现一次,并且模板不能再次用于更新的变量。就像你会做的那样:
>>> str_template = '{i} squared: {n}'
>>> for i in range(2, 5):
... print(str_template.format(i=i, n=i**2))
...
2 squared: 4
3 squared: 9
4 squared: 16
另一种情况是使用i18n, where string.Template
。如果没有旧技术,许多用例将是不可能的。享受字符串插值,但它并非适用于所有用例,即需要可重用模板的地方。
如果您想继续支持 python 3.5,您可以使用 fstring
pip install fstring
from fstring import fstring
x = 1
y = 2.0
plus_result = "3.0"
print fstring("{x}+{y}={plus_result}")
# Prints: 1+2.0=3.0
我怀疑 str.format() 会被弃用,因为这将是现有项目的噩梦。话虽这么说...
string = f'This is a {object}'
比
更易读
string = 'This is a {}'.format(object)
所以我说尽可能使用 f-strings。
我在我的 Python 3.5 项目中经常使用 .format()
,但我担心它会在下一个 Python 版本中被弃用,因为 f-字符串,一种新的字符串文字。
>>> name = "Test"
>>> f"My app name is {name}."
'My app name is Test.'
格式化字符串功能是否可以完全取代旧的 .format()
?从现在开始,在所有情况下都使用新样式会更好吗?
我理解它基于“简单胜于复杂”的理念。但是,性能问题呢?它们之间有什么区别吗?或者它只是简单的外观相同的功能?
I'm afraid that it will be deprecated during the next Python versions
不要,str.format
不会(也没有理由)很快离开,引入 f
prefixed-strings 的 PEP 甚至 states in its Abstract:
This PEP does not propose to remove or deprecate any of the existing string formatting mechanisms.
格式化字符串的引入是为了解决其他格式化字符串方法的一些缺点;不要扔掉旧方法并强迫天知道有多少项目要使用 f-string,如果他们希望他们的代码适用于 Python 3.6+.
至于它们的性能,我最初怀疑它们可能更慢似乎是错误的,f-strings 似乎很容易胜过它们的 .format
对应物:
➜ cpython git:(master) ./python -m timeit -s "a = 'test'" "f'formatting a string {a}'"
500000 loops, best of 5: 628 nsec per loop
➜ cpython git:(master) ./python -m timeit "'formatting a string {a}'.format(a='test')"
100000 loops, best of 5: 2.03 usec per loop
在撰写本文时,这些是针对 CPython 存储库的主分支完成的;它们肯定会发生变化:
f-strings
,作为一项新功能,可能有可能的优化- 对 CPython 的优化可能会使
.format
更快(例如 Speedup method calls 1.2x)
但真的,不要太担心速度,担心什么对你和其他人来说更具可读性。
在许多情况下,这将是 f-strings
,但 format
更好。
为了基于 Jim 的回答并解决您的性能问题,我使用了 python 的 dis
模块来比较两个语法不同但功能相同的函数的字节码指令。
import dis
def f1():
a = "test"
return f"{a}"
def f2():
return "{a}".format(a='test')
print(dis.dis(f1))
print(dis.dis(f2))
其结果是:
11 0 LOAD_CONST 1 ('test') 2 STORE_FAST 0 (a) 12 4 LOAD_FAST 0 (a) 6 FORMAT_VALUE 0 8 RETURN_VALUE None 15 0 LOAD_CONST 1 ('{a}') 2 LOAD_ATTR 0 (format) 4 LOAD_CONST 2 ('test') 6 LOAD_CONST 3 (('a',)) 8 CALL_FUNCTION_KW 1 10 RETURN_VALUE None
可以看出,f 字符串在没有属性或函数调用的情况下处理格式,这会造成类型检查和内存开销。根据 timeit
这导致大约 3 倍的性能提升(对于我的特定功能)
>>> timeit.timeit('f1()', 'from __main__ import f1', number=100000) 0.012325852433775708 >>> timeit.timeit('f2()', 'from __main__ import f2', number=100000) 0.036395029920726074
有一件事没有提到,这使得旧技术不可能被弃用,那就是插值仅适用于字符串文字。意思是,字符串在运行时呈现一次,并且模板不能再次用于更新的变量。就像你会做的那样:
>>> str_template = '{i} squared: {n}'
>>> for i in range(2, 5):
... print(str_template.format(i=i, n=i**2))
...
2 squared: 4
3 squared: 9
4 squared: 16
另一种情况是使用i18n, where string.Template
。如果没有旧技术,许多用例将是不可能的。享受字符串插值,但它并非适用于所有用例,即需要可重用模板的地方。
如果您想继续支持 python 3.5,您可以使用 fstring
pip install fstring
from fstring import fstring
x = 1
y = 2.0
plus_result = "3.0"
print fstring("{x}+{y}={plus_result}")
# Prints: 1+2.0=3.0
我怀疑 str.format() 会被弃用,因为这将是现有项目的噩梦。话虽这么说...
string = f'This is a {object}'
比
更易读string = 'This is a {}'.format(object)
所以我说尽可能使用 f-strings。