Gre*_*reg 158 python arrays linked-list list python-internals
它是一个链表,一个数组?我四处搜寻,只发现有人在猜测.我的C知识不足以查看源代码.
Fre*_*Foo 211
实际上,C代码非常简单.扩展一个宏并修剪一些不相关的注释,基本结构在listobject.h,它将列表定义为:
typedef struct {
PyObject_HEAD
Py_ssize_t ob_size;
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
*/
Py_ssize_t allocated;
} PyListObject;
Run Code Online (Sandbox Code Playgroud)
PyObject_HEAD包含引用计数和类型标识符.所以,它是一个过度分配的向量/数组.这个数组填满时调整大小的代码listobject.c.它实际上并没有使数组加倍,而是通过分配来增长
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;
Run Code Online (Sandbox Code Playgroud)
每次都是容量,newsize请求的大小在哪里(不一定allocated + 1是因为你可以extend通过任意数量的元素而不是append逐个"逐个").
另请参阅Python常见问题解答.
小智 46
这是一个动态阵列.实际证明:无论索引如何,索引都需要(当然,差异极小(0.0013μs!))):
...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop
...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop
Run Code Online (Sandbox Code Playgroud)
如果IronPython或Jython使用链表,我会感到震惊 - 它们会破坏许多广泛使用的库的性能,这些库建立在列表是动态数组的假设之上.
Nul*_*ion 29
这取决于实现,但IIRC:
ArrayList因此,他们都有O(1)随机访问.
小智 21
我建议Laurent Luce的文章"Python列表实现".对我来说真的很有用,因为作者解释了如何在CPython中实现列表并为此目的使用优秀的图表.
列出对象C结构
CPython中的列表对象由以下C结构表示.ob_item是列表元素的指针列表.assigned是内存中分配的槽数.
typedef struct {
PyObject_VAR_HEAD
PyObject**ob_item;
Py_ssize_t已分配;
PyListObject;
注意分配的插槽与列表大小之间的区别非常重要.列表的大小与len(l)相同.已分配的插槽数是已在内存中分配的数量.通常,您会看到分配的大小可能大于大小.这是为了避免每次将新元素附加到列表时都需要调用realloc.
...
附加
我们继续添加一个元素:l.append(2).使用n + 1 = 2调用list_resize,但由于分配的大小为4,因此无需分配更多内存.当我们再添加2个整数时会发生同样的事情:l.append(3),l.append(4).下图显示了到目前为止我们所拥有的内容.
...
插入
...
流行的
弹出最后一个元素时:l.pop(),调用listpop().list_resize在listpop()内部调用,如果新大小小于分配大小的一半,则列表缩小.
您可以观察到插槽4仍然指向整数,但重要的是列表的大小现在是4.让我们再弹出一个元素.在list_resize()中,size - 1 = 4 - 1 = 3小于分配的插槽的一半,因此列表缩小为6个插槽,列表的新大小现在为3.
...
正如其他人在上面所述,列表(当明显很大时)是通过分配固定数量的空间来实现的,如果该空间应该填充,则分配更大的空间并复制元素.
要理解为什么方法是O(1)摊销,而不失一般性,假设我们已插入a = 2 ^ n个元素,我们现在必须将表加倍到2 ^(n + 1)大小.这意味着我们目前正在进行2 ^(n + 1)次操作.最后一个副本,我们做了2 ^ n个操作.在那之前我们做了2 ^(n-1)......一直到8,4,2,1.现在,如果我们加上这些,我们得到1 + 2 + 4 + 8 + ... + 2 ^(n + 1)= 2 ^(n + 2) - 1 <4*2 ^ n = O(2 ^ n)= O(a)总插入(即O(1)摊销时间).此外,应该注意的是,如果表允许删除,则表收缩必须以不同的因子(例如3x)完成