Резюме
Кортежи имеют тенденцию работать лучше, чем списки почти в каждой категории:
1) Кортежи могут быть постоянно сложенными .
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
Здесь - как хранится кортеж (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;
Здесь - как хранится список [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;
Обратите внимание, что объект кортежа включает два указателя данных напрямую, в то время как объект списка имеет дополнительный уровень косвенности к внешнему массиву, содержащему два указателя данных.