万物之中, 希望至美.

Python连接字符串优先使用join而不是+

2018.02.28

字符串处理在大多数编程语言中都是不可避免的,而字符串的连接在编程的过程中会经常遇到。Python 中的字符串与其他语言的字符串有点不一样,如:C++,Java,在 Python 中字符串是不可变对象,创建之后便不可以修改了,因此,这个特性会影响到字符串在连接时的效率问题。

常用的字符串连接方法

  1. 使用操作符+连接字符串:

    In [1]: str1, str2, str3 = 'string1 ', 'string2 ', 'string3 '
    
    In [2]: str1 + str2 + str3
    Out[2]: 'string1 string2 string3'
    
  2. 使用join方法连接字符串:

    In [1]: str1, str2, str3 = 'string1 ', 'string2 ', 'string3 '
    
    In [2]: ''.join([str1, str2, str3])
    Out[2]: 'string1 string2 string3'
    

上述两种方式产生了相同的结果,那么除了形式不同外,它们的效率是否一样呢?

性能测试

import timeit

# 100000是字符串连接数目,对应后面的测试数据
str_list = ['this is a long string used for testing.' for n in range(100000)]


# 使用 join 方法连接 str_list 中的元素
def join_test():
    return ''.join(str_list)


# 使用 + 进行字符串连接
def plus_test():
    result = ''
    for item in str_list:
        result = result + item
    return result


if __name__ == '__main__':
    join_timer = timeit.Timer('join_test()', 'from __main__ import join_test')
    print(join_timer.timeit(100))
    plus_timer = timeit.Timer('plus_test()', 'from __main__ import plus_test')
    print(plus_timer.timeit(100))

给上述代码传入一组测试参数(10,100,1000,10000,100000),用于测试join_test()plus_test()这两个方法在进行字符串连接时所用时间的变化,分别记录每次测试的结果:

连接的字符串数量 join_test()运行时间 plus_test()运行时间
10 4.9165013479068875e-05 0.00016158400103449821
100 0.00013869800022803247 0.001382818998536095
1000 0.0009924670157488436 0.015010426985099912
10000 0.01003621399286203 0.16328153299400583
100000 0.08696807900560088 1.762425111985067

结果分析

从测试结果可以看到使用join()方法和使用+操作符来连接字符串时,join()方法的效率明显要高于+操作符。那么,造成这种差别的原因在哪里呢?

在使用+操作符时,由于字符串是不可变对象,其工作原理应该是这样:如果要连接如下字符串时:s1 + s2 + s3 + …… + sn,执行一次+操作便会在内存中申请一块新的内存空间,并将上次操作的结果和本次操作的右操作数复制到新申请的内存空间,即当执行 s1 + s2 时,会申请一块内存空间,将 s1、s2 复制到该内存中,执行 s1 + s2 + s3 时,会申请一块内存空间,将 s1 + s2 的结果和 s3 复制到该内存中,依次类推。在 N 个字符串的连接过程中,会产生N-1个中间结果,每一个中间结果都要申请和复制一次内存,因此严重的影响了执行效率。

而当用joio()方法连接字符串时,会先计算出需要的总的内存空间,然后一次性申请内存空间并将字符串复制到申请的内存空间中,减少了中间结果,所以执行效率较高。

因此,在字符串的连接过程中,特别是大规模字符串的连接,应该优先使用join而不是+。因水平有限,以上分析说的不对还请指出。

comments powered by Disqus