今天在课堂上,我们了解到在Python中从列表中检索元素是O(1)
。为什么会这样呢?假设我有四个项目的列表,例如:
li = ["perry", 1, 23.5, "s"]
这些项目在内存中具有不同的大小。因此,不可能取li[0]
的存储位置并添加每个元素大小的三倍来获得li[3]
的存储位置。那么,解释器如何知道li[3]
的位置而不必遍历列表以检索元素?
答案 0 :(得分:53)
Python中的列表被实现为指针 1 的数组。因此,创建列表时实际上发生了什么:
["perry", 1, 23.5, "s"]
实际上是在创建一个指针数组,如下所示:
[0xa3d25342, 0x635423fa, 0xff243546, 0x2545fade]
每个指针“指向”内存中的各个对象,因此字符串"perry"
将存储在地址0xa3d25342
中,数字1
将存储在{{1}中}等
由于所有指针的大小相同,因此解释器可以实际上将元素大小的3倍加到0x635423fa
的地址上,以获取存储在{{1 }}。
1 从以下位置获取更多详细信息:the horse's mouth (CPython source code on GitHub)。
答案 1 :(得分:17)
您说a = [...]
时,a
实际上是指向PyObject
的指针,其中包含指向PyObject
s的指针的数组。
当您请求a[2]
时,解释器首先跟随指针指向列表的PyObject
,然后将2
添加到列表中的数组的地址,然后返回该指针。如果您要求输入a[0]
或a[9999]
,也会发生同样的情况。
基本上,所有Python对象都是通过引用而不是值来访问的,甚至是2
之类的整数文字。指针系统中只有一些技巧可以保持所有效率。而且指针的大小是已知的,因此可以方便地将它们存储在C样式的数组中。
答案 2 :(得分:6)
简短的回答:Python列表是数组。
长答案:计算机科学术语 list 通常是指单链接列表(如在函数编程中使用)或双链接列表(如在程序编程中使用)。这些数据结构支持O(1)插入到列表的开头(从功能上)或在不需要搜索的任何位置(从过程上)。 Python``列表''没有这些特征。相反,它支持在列表末尾附加(摊销)O(1)(例如C ++ std :: vector或Java ArrayList)。 Python列表实际上是CS术语中可调整大小的数组。
以下评论from the Python documentation解释了Python``列表''的一些性能特征:
也可以将列表用作队列,其中添加的第一个元素是检索到的第一个元素(“先进先出”);但是,列表对于此目的并不有效。尽管从列表末尾开始的添加和弹出很快速,但从列表开始进行插入或弹出很慢(因为所有其他元素都必须移动一个)。