问题描述
连接两个列表时,
a = [0......,10000000]
b = [0......,10000000]
a = a + b
Python 运行时是否会分配一个更大的数组并循环遍历两个数组并将 a
和 b
的元素放入更大的数组中?
还是循环遍历 b
的元素并将它们附加到 a
并根据需要调整大小?
我对 cpython 实现感兴趣。
解决方法
可以通过查看找出id
的{{1}}之前和之后串联a
:
b
这里,由于>>> a = [1,2,3]
>>> b = [4,5,6]
>>> id(a)
140025874463112
>>> a = a + b
>>> id(a)
140025874467144
是不同的,我们看到,解释器创造了一个新的列表,并将其绑定到名为id
。旧a
列表将最终被垃圾收集。
但是,使用增强赋值运算符 a
时的行为可能会有所不同:
+=
这里,由于>>> a = [1,6]
>>> id(a)
140025844068296
>>> a += b
>>> id(a)
140025844068296
是相同的,我们看到,解释器重复使用相同的列表对象id
和所附的值a
给它。
更多详细信息,请参阅以下的问题:
- Why does += behave unexpectedly on lists?
- Does list concatenation with the `+` operator always return a new `list` instance?
在 CPython 中,两个列表在 function list_concat
中连接起来。
您可以在链接的源代码中看到该函数分配了适合两个列表所需的空间。
size = Py_SIZE(a) + Py_SIZE(b);
np = (PyListObject *) list_new_prealloc(size);
然后它将两个列表中的项目复制到新列表中。
for (i = 0; i < Py_SIZE(a); i++) {
...
}
...
for (i = 0; i < Py_SIZE(b); i++) {
...
}
,
您可以在 listobject.c::list_concat 中看到实现。 Python 将获取 a
和 b
的大小并创建一个该大小的新列表对象。然后它将遍历 a
和 b
的值,它们是指向 Python 对象的 C 指针,增加它们的引用计数并将这些指针添加到新列表中。
它将创建一个新列表,其中包含第一个列表中项目的浅表副本,然后是第二个列表中项目的浅表副本。 +
运算符调用 object.__add__(self,other)
方法。例如,对于表达式 x + y
,其中 x
是具有 __add__()
方法的类的实例,x.__add__(y)
被调用。您可以在 documentation 中阅读更多内容。