执行摘要
在几乎所有类别中,元组的性能往往都优于列表:
1) 元组可以是constant folded。
2) 元组可以重复使用而不是复制。
3) 元组是紧凑的,不会过度分配。
4) 元组直接引用它们的元素。
元组可以不断折叠
常量元组可以由 Python 的窥孔优化器或 AST 优化器预先计算。另一方面,列表是从头开始构建的:
>>> from dis import dis
>>> dis(compile("(10, 'abc')", '', 'eval'))
1 0 LOAD_CONST 2 ((10, 'abc'))
3 RETURN_VALUE
>>> dis(compile("[10, 'abc']", '', 'eval'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 ('abc')
6 BUILD_LIST 2
9 RETURN_VALUE
元组不需要复制
运行tuple(some_tuple) 会立即返回。由于元组是不可变的,因此不必复制它们:
>>> a = (10, 20, 30)
>>> b = tuple(a)
>>> a is b
True
相比之下,list(some_list) 要求将所有数据复制到一个新列表中:
>>> a = [10, 20, 30]
>>> b = list(a)
>>> a is b
False
元组不会过度分配
由于元组的大小是固定的,它可以比需要过度分配以提高 append() 操作效率的列表更紧凑地存储。
这给元组一个很好的空间优势:
>>> import sys
>>> sys.getsizeof(tuple(iter(range(10))))
128
>>> sys.getsizeof(list(iter(range(10))))
200
这是来自 Objects/listobject.c 的注释,它解释了列表的作用:
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
* Note: new_allocated won't overflow because the largest possible value
* is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
*/
元组直接引用它们的元素
对对象的引用直接合并到元组对象中。相比之下,列表对外部指针数组有一个额外的间接层。
这使元组在索引查找和解包方面具有较小的速度优势:
$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'a[1]'
10000000 loops, best of 3: 0.0304 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'a[1]'
10000000 loops, best of 3: 0.0309 usec per loop
$ python3.6 -m timeit -s 'a = (10, 20, 30)' 'x, y, z = a'
10000000 loops, best of 3: 0.0249 usec per loop
$ python3.6 -m timeit -s 'a = [10, 20, 30]' 'x, y, z = a'
10000000 loops, best of 3: 0.0251 usec per loop
Here 是元组(10, 20) 的存储方式:
typedef struct {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
Py_ssize_t ob_size;
PyObject *ob_item[2]; /* store a pointer to 10 and a pointer to 20 */
} PyTupleObject;
Here 是列表[10, 20] 的存储方式:
PyObject arr[2]; /* store a pointer to 10 and a pointer to 20 */
typedef struct {
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
Py_ssize_t ob_size;
PyObject **ob_item = arr; /* store a pointer to the two-pointer array */
Py_ssize_t allocated;
} PyListObject;
请注意,元组对象直接合并了两个数据指针,而列表对象对保存两个数据指针的外部数组有一个额外的间接层。