将串联字符串分配给python中的变量时,为什么这么慢?

时间:2019-05-11 01:21:35

标签: python append big-o string-concatenation

如果仅是如下所示的字符串连接,则立即完成。

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给出相同的结果。

1 个答案:

答案 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 += ba = a + b的语句,请不要依赖CPython有效地实现就地字符串连接。即使在CPython中,这种优化也很脆弱(仅适用于某些类型),并且在不使用引用计数的实现中根本没有这种优化。在库的性能敏感部分,应使用''.join()形式。这样可以确保在各种实现方式中串联都在线性时间内发生。

  •   

关于“仅适用于某些类型”的注释很重要。此优化仅适用于str;在Python 2中,它不适用于unicode(即使Python 3的str基于Python 2的unicode的实现),而在Python 3中,它不适用于{ {1}}(与Python 2的bytes相似)。