Кортеж против списка в Python: что и когда использовать
У обоих есть индексы, len и in, но список можно менять, а кортеж — нет. Четыре отличия: изменяемость, ключ словаря, память и правило выбора за три секунды.
Редакция Питоники
Кортеж из урока 11 и список — близнецы с разным характером: оба хранят упорядоченные значения с доступом по индексу, но один застыл, а второй живой. Разберём, что у них общее, чем приходится платить и как выбирать между ними за три секунды.
Читать умеют одинаково
lst = [1, 2, 3]
tup = (1, 2, 3)
print(lst[0], tup[0])
print(len(lst) == len(tup))
print(2 in tup)
print(lst[1:], tup[1:])
1 1 True True [2, 3] (2, 3)
Индексы, len, in, срезы — без разницы, список или кортеж: последовательности читаются одними и теми же средствами. Расходится всё на попытке изменить:
lst = [1, 2, 3]
tup = (1, 2, 3)
lst.append(4)
print(lst)
try:
tup.append(4)
except AttributeError as e:
print('AttributeError:', e)
[1, 2, 3, 4] AttributeError: 'tuple' object has no attribute 'append'
Ключ словаря: кортеж да, список нет
Словарь требует от ключа постоянства: хеш считается при добавлении и больше не пересчитывается. Кортеж постоянен — годится; список меняется — Python отказывается с TypeError: unhashable type. Так собирают словари с составными ключами:
offices = {('Москва', 'Арбат'): 12, ('Казань', 'Баумана'): 4}
print(offices[('Казань', 'Баумана')])
print(offices['Москва', 'Арбат'])
print(len(offices))
4 12 2
Скобки в индексе можно опустить: offices['Москва', 'Арбат'] — Python сам соберёт ключ-кортеж. Параллель с уроком 10 полная: там frozenset заменил set в ключах, здесь tuple заменяет list.
offices = {['Москва', 'Арбат']: 12}
Traceback (most recent call last):
File "app.py", line 1, in <module>
offices = {['Москва', 'Арбат']: 12}
^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'list'Память и скорость
Кортеж компактнее списка: он хранит ровно свои элементы, без запаса на будущие добавления — список заранее резервирует место под append. Создаётся кортеж тоже быстрее: Python собирает его за одну операцию, без построения пустого объекта с последующим заполнением. На паре значений разница копеечная, на миллионах записей — уже заметная. Проверить размер конкретного значения можно функцией sys.getsizeof из стандартной библиотеки, но правило проще живых цифр: данные без изменений — кортеж дешевле по всем статьям.
Правило выбора
Правило выбора простое: если набор значений фиксирован — берите кортеж, если коллекция будет расти и меняться — список. Координаты, цвета, даты, записи базы — кортежи. Корзина, лог, список задач — списки. Кортеж ещё и самодокументация: увидели (x, y) в коде — значит, менять это значение никто не собирался.
| Ситуация | Выбор | Почему |
|---|---|---|
| Координаты, RGB, дата | кортеж | состав застыл навсегда |
| Корзина, лог, задачи | список | append и remove по ходу дела |
| Ключ словаря | кортеж | хешируемость |
| Накопление в цикле | список | результат растёт |
| Возврат нескольких значений | кортеж | готов к распаковке из урока 13 |
invoice = ('счёт 12', 4500)
cart = ['хлеб', 'молоко']
cart.append('чай')
print(cart)
print(invoice)
['хлеб', 'молоко', 'чай']
('счёт 12', 4500)Конверсии и сортировка
sorted не умеет менять на месте и не пытается: кортеж она не трогает, а возвращает новый список. Исходный порядок при этом цел:
route = ('Казань', 'Москва', 'Сочи')
alpha = sorted(route)
print(route)
print(alpha)
('Казань', 'Москва', 'Сочи')
['Казань', 'Москва', 'Сочи']Нужна отсортированная версия именно как кортеж — tuple(sorted(route)). Обратная конверсия тоже на месте:
lst = [1, 2, 3]
frozen = tuple(lst)
lst.append(4)
print(frozen)
print(lst)
(1, 2, 3) [1, 2, 3, 4]
tuple(lst) сделал снимок: добавление в список не задело кортеж. Конверсия недорогая, но помнит момент вызова — так же ведёт себя frozenset из урока 10.
Тонкий момент: список внутри кортежа
Неизменяемость кортежа — про его ячейки, а не про содержимое: в ячейке лежит ссылка, и объект по ссылке может быть живым. Положим в кортеж список и добавим в него элемент:
record = ('Анна', [90, 85])
record[1].append(78)
print(record)
('Анна', [90, 85, 78])Кортеж не менялся: в его ячейке та же ссылка на список — изменился сам список. Отсюда правило: ключом словаря может быть только кортеж с неизменяемыми элементами внутри — кортеж со списком упадёт с тем же unhashable.
Что дальше
Выбор между кортежем и списком сделан. Дальше — самое приятное свойство кортежей: в уроке 13 они разбираются на переменные одной строкой — x, y = point: распаковка, обмен значений и звёздочка для остатка.
Сначала предскажи ответ в голове — это главный навык программиста.
data = [1, 2, 3]
fixed = tuple(data)
data.append(4)
print(fixed)
print(len(data))
box = {}
items = ('книга', 'ручка')
box[items] = 2
print(box[items] + 1)
t = ('a', ['b'])
t[1].append('c')
print(t)
print(len(t))
1. Что общего у списка и кортежа?
2. Главное отличие кортежа от списка?
3. Почему список нельзя ключом словаря?
4. Что происходит в записи offices['Москва', 'Арбат']?
5. Что вернёт sorted((3, 1, 2))?
6. record = ('Анна', [90]). Что произойдёт при record[1].append(5)?
7. Для растущего лога событий что выбрать?
8. Что вернёт tuple([1, 2, 3]) == (1, 2, 3)?
Даны точки p1 = (1, 2) и p2 = (4, 6). Соберите словарь places с ключами-кортежами: p1 — старт, p2 — финиш. Выведите places[p1], places[p2] и число записей.
Когда использовать кортеж, а когда список?
Фиксированный набор значений — кортеж: координаты, даты, записи. Растущая и меняющаяся коллекция — список: корзина, лог, задачи. Кортеж дополнительно нужен там, где требуется хешируемость: ключ словаря или элемент множества.
Урок 11: tuple — неизменяемый кортежПочему список нельзя ключом словаря?
Словарь хеширует ключ при добавлении и полагается на его постоянство. Список изменяем — после append он разъехался бы со своим хешем, поэтому Python запрещает его с TypeError: unhashable type: 'list'. Кортеж из хешируемых элементов работает ключом.
Урок 1 курса про словариБыстрее ли кортеж списка?
Да, незначительно, но стабильно: кортеж компактнее в памяти — без запаса на будущие добавления, и создаётся за одну операцию. На маленьких данных разница не видна, на миллионах записей — ощутима.
Как превратить список в кортеж и обратно?
tuple(lst) — снимок-кортеж на момент вызова, list(t) — изменяемый список из кортежа. Изменения исходника после конверсии на результат не влияют: это независимые объекты.
Может ли кортеж содержать список?
Может: ('Анна', [90, 85]) — корректная запись. Но неизменяемость распространяется только на ячейки кортежа: список по ссылке остаётся изменяемым. Такой кортеж не работает ключом словаря — хеш требует неизменяемости по всей глубине.
Понравился урок? Сошлитесь на него
«Правило выбора простое: если набор значений фиксирован — берите кортеж, если коллекция будет расти и меняться — список.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Множества · Урок 11
Tuple в Python: неизменяемый кортеж и доступ по индексу
Кортеж хранит фиксированный набор значений и защищает его от изменений: создали — и состав застыл. Создание, доступ по индексу, IndexError и то, почему запятая главнее скобок.
Множества · Урок 13
Распаковка кортежей в Python: x, y = point
Один кортеж — несколько переменных за один шаг: x, y = point. Классический обмен значений, звёздочка для остатка и почему распаковывать множества опасно.
Словари · Урок 8
Перебор словаря: keys, values и items
Три представления словаря — keys, values, items: перебираем цены в цикле for, распаковываем пары в две переменные, считаем сумму и фильтруем дешёвые позиции.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Словари · Урок 18
Ключи: что можно и что нельзя
Ключом бывают строки, числа и кортежи, но не список: TypeError: unhashable type объясняется устройством словаря, а frozenset спасает, когда нужен набор.
ключи словаря pythonunhashable type list
ООП · Урок 12
Сравнение объектов: __eq__ и __hash__
По умолчанию == для своих классов работает как is: два одинаковых счёта оказываются «не равны». Определяем __eq__ по полям, добавляем __hash__ — и объекты работают в словарях и множествах.
объект как ключ словаря pythonunhashable type python
Множества · Урок 10
frozenset: неизменяемое множество в Python
Множество, которое нельзя менять: frozenset создаётся один раз, поддерживает все проверки и операции чтения и годится туда, куда set не пускают — ключом в словарь и элементом другого множества.
frozenset ключ словаряpython unhashable type