特殊情况下,字符串连接的 += 比 = 更有效

Dor*_*rba 9 python string concatenation string-concatenation python-3.11

我有使用 python 3.11 的代码:

import timeit

code_1 = """
initial_string = ''
for i in range(10000):
    initial_string = initial_string + 'x' + 'y'
"""

code_2 = """
initial_string = ''
for i in range(10000):
    initial_string += 'x' + 'y'
"""

time_1 = timeit.timeit(code_1, number=100)
time_2 = timeit.timeit(code_2, number=100)

print(time_1)
# 0.5770808999950532
print(time_2)
# 0.08363639999879524
Run Code Online (Sandbox Code Playgroud)

为什么在这种情况下+=效率更高?据我所知,串联次数相同,执行顺序不会改变结果。

由于字符串是不可变的,这并不是因为 inplace shinanigans,而且我发现字符串 concat 的唯一问题是.join效率,但我不想要最高效的,只需理解为什么+=看起来比=.

使用此代码,表单之间的性能几乎相同:

import timeit

code_1 = """
initial_string = ''
for i in range(10000):
    initial_string = initial_string + 'x'
"""

code_2 = """
initial_string = ''
for i in range(10000):
    initial_string += 'x'
"""

time_1 = timeit.timeit(code_1, number=100)
time_2 = timeit.timeit(code_2, number=100)

print(time_1)
# 0.07953230000566691
print(time_2)
# 0.08027460001176223
Run Code Online (Sandbox Code Playgroud)

'x' + 'y'我注意到使用不同 Python 版本(形式)的差异:

Python 3.7 到 3.9:

print(time_1)
# ~0.6
print(time_2)
# ~0.3
Run Code Online (Sandbox Code Playgroud)

Python 3.10:

print(time_1)
# ~1.7
print(time_2)
# ~0.8
Run Code Online (Sandbox Code Playgroud)

Python 3.11 进行比较:

print(time_1)
# ~0.6
print(time_2)
# ~0.1
Run Code Online (Sandbox Code Playgroud)

类似但没有回答问题:How is the s=s+c string concat optimization Decision?

如果 s 是字符串,则 s = s + 'c' 可能会就地修改字符串,而 t = s + 'c' 则不能。但是操作 s + 'c' 如何知道它处于哪种场景呢?

简而言之:优化发生在when s = s + 'c',而不是when,t = s + 'c'因为python需要保留对第一个字符串的引用并且不能就地连接。

在这里,我们总是使用简单赋值或增强赋值来对原始字符串进行赋值,因此就地连接应该适用于这两种情况。

use*_*ica 6

一段时间以来,CPython 进行了一项优化,尝试在可能的地方执行字符串连接。Python 版本之间的细节有所不同,有时差别很大 - 例如,它不适用于Python 3.11 上的全局变量,它曾经特定于 Python 2 上的字节串,但它特定于 Python 3 上的 Unicode 字符串。

在 Python 3.10 上,优化从 开始unicode_concatenate,最终到达PyObject_Realloc内部resize_compactresize_inplace,尝试调整左侧操作数的大小。

关于跨 Python 版本的优化,一个相当一致的事情是,只有当串联的左侧没有其他引用,或者唯一的引用是将串联结果分配给的变量时,它才有效。在你缓慢的情况下:

initial_string = initial_string + 'x' + 'y'
Run Code Online (Sandbox Code Playgroud)

的 LHSinitial_string + 'x'initial_string,并且您不会将结果分配回initial_string- 您将'y'首先连接到结果。因此,优化无法启动initial_string + 'x'。(+ 'y'不过,它在这部分中发挥了作用。)

对于您的其他情况,优化有效。例如,在

initial_string += 'x' + 'y'
Run Code Online (Sandbox Code Playgroud)

您可以连接initial_stringand'x'然后连接结果,而不是连接 and 然后附加 。更改后的运算顺序意味着您要将串联结果分配回,因此会应用优化。(而且不断折叠,这有一点帮助,但不是性能差异的主要原因。)'y''x''y'initial_stringinitial_stringinitial_string'x' + 'y'