Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 12 из 20 Начальный 15 мин 100 XP

Кортеж против списка в 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'
Словарь даже не создаётся: ключ-список не хешируется. Оберните список в tuple — ключом станет постоянный кортеж.

Память и скорость

Кортеж компактнее списка: он хранит ровно свои элементы, без запаса на будущие добавления — список заранее резервирует место под 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))
Проверь себя
0 / 8

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)?

Карточки терминов
Запомнено: 0 / 8
Практика

Даны точки p1 = (1, 2) и p2 = (4, 6). Соберите словарь places с ключами-кортежами: p1 — старт, p2 — финиш. Выведите places[p1], places[p2] и число записей.

practice.py
Вопросы и ответы по уроку

Когда использовать кортеж, а когда список?

Фиксированный набор значений — кортеж: координаты, даты, записи. Растущая и меняющаяся коллекция — список: корзина, лог, задачи. Кортеж дополнительно нужен там, где требуется хешируемость: ключ словаря или элемент множества.

Урок 11: tuple — неизменяемый кортеж

Почему список нельзя ключом словаря?

Словарь хеширует ключ при добавлении и полагается на его постоянство. Список изменяем — после append он разъехался бы со своим хешем, поэтому Python запрещает его с TypeError: unhashable type: 'list'. Кортеж из хешируемых элементов работает ключом.

Урок 1 курса про словари

Быстрее ли кортеж списка?

Да, незначительно, но стабильно: кортеж компактнее в памяти — без запаса на будущие добавления, и создаётся за одну операцию. На маленьких данных разница не видна, на миллионах записей — ощутима.

Как превратить список в кортеж и обратно?

tuple(lst) — снимок-кортеж на момент вызова, list(t) — изменяемый список из кортежа. Изменения исходника после конверсии на результат не влияют: это независимые объекты.

Может ли кортеж содержать список?

Может: ('Анна', [90, 85]) — корректная запись. Но неизменяемость распространяется только на ячейки кортежа: список по ссылке остаётся изменяемым. Такой кортеж не работает ключом словаря — хеш требует неизменяемости по всей глубине.

Понравился урок? Сошлитесь на него

«Правило выбора простое: если набор значений фиксирован — берите кортеж, если коллекция будет расти и меняться — список.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.