假设我们有一个字符串列表,我们想通过连接这个列表中的所有元素来创建一个字符串。像这样的东西:
def foo(str_lst):
result = ''
for element in str_lst:
result += element
return result
由于字符串是不可变对象,我希望 python 创建一个新的 str 对象并在每次迭代时复制 result 和 element 的内容。它使O(M * N^2)时间复杂度,M 是每个元素的长度,N 是列表的大小。
但是,我的实验表明它以线性时间运行。
N = 1000000 # 1 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 0.5 seconds
N = 2000000 # 2 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 1.0 seconds
N = 10000000 # 10 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 5.3 seconds
我怀疑 python 在引擎盖下使用了类似 stringbuffer 的东西。因此,它不会在每次迭代时创建新对象。
现在考虑一个稍微不同的实现。唯一的区别是一项额外的任务。
def foo2(str_lst):
result = ''
for element in str_lst:
result += element
temp = result # new added line
return result
我知道该temp = result行不会创建新对象。temp只是指向同一个对象。所以,这个小小的变化应该不会对性能产生太大影响。
N = 1000000 # 1 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 0.5 seconds
foo2(str_lst) # It takes around 30 seconds
N = 2000000 # 2 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 1 seconds
foo2(str_lst) # It takes around 129 seconds
然而,有一个巨大的差异。看起来 foo2 函数是 O(N^2) 而 foo 是 O(N)。
我的问题是 python 如何在不破坏其他语言组件(如不可变对象分配)的情况下实现字符串连接的线性时间?这条额外的线如何影响性能?我在 cpython 实现中搜索了一些,但找不到确切的位置。
UYOU
jeck猫
相关分类