Отличия кортежа от списка в 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%

Что нужно запомнить

  • Кортеж неизменяем, список — изменяем.
  • Кортеж занимает меньше памяти, чем список.
  • Копирование кортежа происходит немного быстрее, чем копирование списка.
  • Используйте кортеж, если вам нужен список, который вы не собираетесь изменять.
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©