该 存储 是“未装箱的”,但是每次访问元素时,Python都必须对其进行“装箱”(将其嵌入常规Python对象中)以便对其进行任何处理。例如,您sum(A)
遍历数组,并将每个整数一次装在一个常规Pythonint
对象中。那要花时间。在中sum(L)
,所有装箱操作均在创建列表时完成。
因此,最后,阵列通常较慢,但所需的内存却少得多。
这是最新版本的Python 3的相关代码,但是自Python首次发布以来,相同的基本思想也适用于所有cpython实现。
这是访问列表项的代码:
PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
/* error checking omitted */
return ((PyListObject *)op) -> ob_item[i];
}
它几乎没有什么:somelist[i]
只返回i
列表中的第一个对象(并且cpython中的所有Python对象都是指向结构的指针,该结构的初始段符合的布局struct PyObject
)。
这__getitem__
是array
带有类型代码的实现l
:
static PyObject *
l_getitem(arrayobject *ap, Py_ssize_t i)
{
return PyLong_FromLong(((long *)ap->ob_item)[i]);
}
原始内存被视为平台本地C
long
整数的矢量;该i
“个C long
被读取起来; 然后PyLong_FromLong()
调用该方法将本机包装(“框”)C long
在Pythonlong
对象中(在Python 3中,该对象消除了Python 2int
和之间的区别long
,实际上显示为type int
)。
这种装箱必须为Pythonint
对象分配新的内存,然后将nativeC long
的位喷射到其中。在原始示例的上下文中,此对象的生存期非常短(足够长,足以sum()
将内容添加到正在运行的总计中),然后需要更多的时间来取消分配新int
对象。
这就是速度差异的来源,在cpython实现中,总是如此,而且总是如此。