我以为 array.array 比 list 要快,因为array看起来是未装箱的(unboxed)。
然后,我得到了下面的结果:In [1]: import arrayIn [2]: L = list(range(100000000))In [3]: A = array.array('l', range(100000000))In [4]: %timeit sum(L)
1 loop, best of 3: 667 ms per loopIn [5]: %timeit sum(A)
1 loop, best of 3: 1.41 s per loopIn [6]: %timeit sum(L)
1 loop, best of 3: 627 ms per loopIn [7]: %timeit sum(A)
1 loop, best of 3: 1.39 s per loop
这种区别的原因是什么?
其存储是“未装箱的”,但每当你访问一个元素的时候,Python必须将它“装箱”(将之嵌入在一个普通的Python对象中),以便做任何事情。 例如,sum(A)遍历了array,并且一次一个地把每个证书装箱到一个普通的Python int对象中。这要花费时间。而在sum(L)中,所有的装箱都已在创建列表时完成了。
所以最后,数组通常较慢,但是相较需要相当少的内存。
----------------------------------------------------------------------------------------------------------------
这是Python 3最近版本的相关代码,但是相同的基本思想适用于所有CPython实现。
以下是访问列表项的代码:PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
/* error checking omitted */
return ((PyListObject *)op) -> ob_item[i];
}
它做的事很少:somelist [i] 仅仅返回列表中的第i个对象(CPython中的所有Python对象都是指向一个结构体的指针,其初始段符合一个PyObject结构体的结构)。下面是具有类型代码 l 的 array 的__getitem__实现: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 包装(“装箱”)成Python long 对象(在Python 3中,它消除了Python 2中 int 和 long 之间的区别,实际上显示为int)。这个装箱必须为Python int对象分配新的内存,并将本地的C long的位写入其中。在原例的上下文中,这个对象的生命周期非常短暂(只是足够让sum()将内容添加到总数中),然后需要更多的时间来释放新的int对象。这就是速度差异的来源,总是来自于,而且总将来自于CPython的实现。