Отличия кортежа от списка в Python
В этой статье вы узнаете, в чем отличия кортежа от списка в Python.
Кортеж и список относятся к типу последовательностей. Однако у них есть несколько существенных различий.
1) Кортеж — неизменяемый, список — изменяемый
Меняем список
Код ниже изменяет первый элемент списка.
fruits = ['яблоко', 'апельсин', 'банан']
fruits[0] = 'клубника'
print(fruits)
Вывод
['клубника', 'апельсин', 'банан']
Как вы видите, элемент списка успешно изменился.
Не можем изменить кортеж
Однако изменить элемент кортежа не получится. Если попробовать это сделать, как в примере ниже, получим ошибку:
fruits = ('яблоко', 'апельсин', 'банан')
fruits[0] = 'клубника' # Ошибка TypeError: 'tuple' object does not support item assignment
Кортеж можно пересоздать, если нужны изменения
В Python нельзя изменить элемент кортежа, но вы можете задать новый кортеж и ссылаться на него. Например, так:
fruits = ('яблоко', 'апельсин', 'банан')
fruits = ('клубника', 'апельсин', 'банан')
В этом примере Python создает новый кортеж и привязывает переменную fruits
к нему.
Если вы изучите адреса памяти объектов кортежа, то увидите, что переменная fruits
ссылается на другой адрес памяти после присваивания:
fruits = ('яблоко', 'апельсин', 'банан')
print(hex(id(fruits)))
fruits = ('клубника', 'апельсин', 'банан')
print(hex(id(fruits)))
Вывод
0x1c018286e00
0x1c018286e40
2) Кортежи занимают меньше памяти, чем списки
Список — неизменяемая последовательность. Это означает, что в него можно добавлять новые элементы. Из-за этого Python приходится выделять для списка больше памяти, чем нужно. Это называется перераспределением. Избыточное выделение повышает производительность при расширении списка, но увеличивает объект памяти.
Кортеж — неизменяемая последовательность, поэтому количество его элементов фиксировано. Это позволяет Python просто выделить достаточное количество памяти для хранения элементов, заданных при создании.
В результате кортеж в большинстве случае занимает меньше памяти, чем список.
Давайте докажем это с помощью функции getsizeof()
из модуля sys
.
from sys import getsizeof
fruits = ['яблоко', 'апельсин', 'банан']
print(f'Размер памяти для списка — {getsizeof(fruits)} байтов.')
fruits = ('яблоко', 'апельсин', 'банан')
print(f'Размер памяти для списка — {getsizeof(fruits)} байтов.')
Вывод
Размер памяти для списка — 80 байтов.
Размер памяти для кортежа — 64 байтов.
3) Копирование кортежа быстрее, чем списка
Когда вы копируете список, Python создает новый список. Рассмотрим этот процесс на следующем примере:
fruit_list = ['яблоко', 'апельсин', 'банан']
fruit_list2 = list(fruit_list)
print(id(fruit_list) == id(fruit_list2)) # Вывод: False
Однако когд вы копируте кортеж, Python повторно использует уже существующий кортеж. Он не создает новый кортеж, поскольку кортежи неизменяемы.
fruit_tuple = ('яблоко', 'апельсин', 'банан')
fruit_tuple2 = tuple(fruit_tuple)
print(id(fruit_tuple) == id(fruit_tuple2)) # Вывод: True
В результате копирование кортежа всегда проходит немного быстрее, чем копирование списка.
Давайте проверим это на следующем примере. Сравним время, необходимое для копирования списка и кортежа 1 миллион раз:
from timeit import timeit
times = 1_000_000
t1 = timeit("list(['яблоко', 'апельсин', 'банан'])", number=times)
print(f'Время для копирования списка {times} раз: {t1}')
t2 = timeit("tuple(('яблоко', 'апельсин', 'банан'))", number=times)
print(f'Время для копирования кортежа {times} раз: {t2}')
diff = "{:.0%}".format((t2 - t1)/t1)
print(f'Разница: {diff}')
Вывод
Время для копирования списка 1000000 раз: 0.12854695800000115
Время для копирования кортежа 1000000 раз: 0.03695795800000212
Разница: -71%
Что нужно запомнить
- Кортеж неизменяем, список — изменяем.
- Кортеж занимает меньше памяти, чем список.
- Копирование кортежа происходит немного быстрее, чем копирование списка.
- Используйте кортеж, если вам нужен список, который вы не собираетесь изменять.