如果仅是如下所示的字符串连接,则立即完成。
test_str = "abcdefghijklmn123456789"
str1 = ""
str2 = ""
start = time.time()
for i in range(1, 100001):
str1 = str1 + test_str
str2 = str2 + test_str
if i % 20000 == 0:
print("time(sec) => {}".format(time.time() - start))
start = time.time()
恒定的处理时间
time(sec) => 0.013324975967407227
time(sec) => 0.020363807678222656
time(sec) => 0.009979963302612305
time(sec) => 0.01744699478149414
time(sec) => 0.0227658748626709
无法解释的是,将串联字符串分配给另一个变量会使过程越来越慢。
test_str = "abcdefghijklmn123456789"
str1 = ""
str2 = ""
start = time.time()
for i in range(1, 100001):
str1 = str1 + test_str
# str2 = str2 + test_str
# ↓
str2 = str1
if i % 20000 == 0:
print("time(sec) => {}".format(time.time() - start))
start = time.time()
处理时间将会延迟。
time(sec) => 0.36466407775878906
time(sec) => 1.105351209640503
time(sec) => 2.6467738151550293
time(sec) => 5.891657829284668
time(sec) => 9.266698360443115
python2和python3给出相同的结果。
答案 0 :(得分:21)
通常,Python语言标准在此不做任何保证;实际上,按照定义,字符串是不可变的,并且您正在做的事情应该用任何一种方式咬住您,就像您编写了Schlemiel the Painter's algorithm的形式一样。
但是在第一种情况下,作为实现细节,CPython(参考解释器)将为您提供帮助,并在一些相当特定的条件下将字符串连接到位(技术上违反了不变性保证),使它能够遵守不变性规则的精神。最重要的条件是被连接的字符串只能在一个位置引用(如果不是,则另一个引用将在适当位置更改,从而违反了str
的外观是不可变的)。通过在每个串联之后分配str2 = str1
,可以确保串联时有两个引用。因此,每个串联都必须创建一个新的str
保留字符串的表观不变性。这意味着更多的内存分配和释放,更多(并逐渐增加)的内存副本,等等。
请注意,PEP 8, the Python style guide中明确建议不要依赖此优化:
应以不损害其他Python实现方式(PyPy,Jython,IronPython,Cython,Psyco等)的方式编写代码。
例如,对于格式为
a += b
或a = a + b
的语句,请不要依赖CPython有效地实现就地字符串连接。即使在CPython中,这种优化也很脆弱(仅适用于某些类型),并且在不使用引用计数的实现中根本没有这种优化。在库的性能敏感部分,应使用''.join()
形式。这样可以确保在各种实现方式中串联都在线性时间内发生。
关于“仅适用于某些类型”的注释很重要。此优化仅适用于str
;在Python 2中,它不适用于unicode
(即使Python 3的str
基于Python 2的unicode
的实现),而在Python 3中,它不适用于{ {1}}(与Python 2的bytes
相似)。