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

uma*_*a66 15 python big-o append 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()
Run Code Online (Sandbox Code Playgroud)

恒定的处理时间

time(sec) => 0.013324975967407227
time(sec) => 0.020363807678222656
time(sec) => 0.009979963302612305
time(sec) => 0.01744699478149414
time(sec) => 0.0227658748626709
Run Code Online (Sandbox Code Playgroud)

莫名其妙地,将串联字符串分配给另一个变量会使过程变得越来越慢。

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()
Run Code Online (Sandbox Code Playgroud)

处理时间将被延迟。

time(sec) => 0.36466407775878906
time(sec) => 1.105351209640503
time(sec) => 2.6467738151550293
time(sec) => 5.891657829284668
time(sec) => 9.266698360443115
Run Code Online (Sandbox Code Playgroud)

python2和python3给出相同的结果。

Sha*_*ger 22

通常,Python语言标准在此不做任何保证;事实上,定义,字符串是不变的,你在做什么应该咬你无论哪种方式,因为你写的形式,倒楣的画家算法

但是,在第一种情况下,作为实现细节,CPython(参考解释器)将为您提供帮助,并在一些相当特定的条件下将字符串连接到位(技术上违反了不变性保证),从而使其能够遵守主题的精神。不变性规则。最重要的条件是被连接的字符串只能在一个位置引用(如果不是,则另一个引用将在适当位置更改,从而违反了str不可变的外观)。通过str2 = str1在每次串联后分配,可以确保串联时有两个引用,因此str 必须由每个串联组成,以保留字符串的表观不变性。这意味着更多的内存分配和释放,更多(并逐渐增加)的内存副本,等等。

请注意,Python样式指南PEP 8中明确建议不要依赖此优化:

  • 应该以不损害Python其他实现(PyPy,Jython,IronPython,Cython,Psyco等)的方式编写代码。

    例如,不要依赖CPython对a += b或形式的语句的就地字符串连接的有效实现a = a + b。即使在CPython中,这种优化也是脆弱的(仅适用于某些类型),并且在不使用引用计数的实现中根本不存在这种优化。在库的性能敏感部分中,''.join()应使用表格代替。这将确保在各种实现中串联发生在线性时间内。

关于“仅对某些类型有效”的注释很重要。此优化仅适用于str; 在Python 2中,它不起作用unicode(即使Python 3 str是基于Python 2的实现unicode),而在Python 3中,它则不起作用bytes(与底层的Python 2类似str)。