Кортежи и распаковка

Теория

Кортеж (tuple) — упорядоченная неизменяемая коллекция. Он похож на список: те же индексы, срезы, len и in. Но изменить кортеж после создания нельзя — ни заменить элемент, ни добавить, ни удалить. Поэтому кортеж подходит для записей с фиксированной структурой, где каждая позиция имеет смысл: позиция заказа (product_id, quantity, price), строка из базы данных, пара «широта, долгота».

С кортежами тесно связана распаковка — присваивание нескольких переменных за раз: product_id, qty, price = item. Число переменных должно совпадать с числом элементов, а оставшиеся элементы можно собрать звёздочкой: first, *rest = .... Распаковка работает с любой последовательностью, но чаще всего встречается с кортежами — в циклах, при обмене значений и (дальше по курсу) при возврате нескольких значений из функции.

Наглядно

Таблица 1. Список и кортеж: чем отличаются и когда что выбирать.

list tuple
Запись [42, 2, 1499] (42, 2, 1499) или 42, 2, 1499
Изменяемость можно менять нельзя менять
Смысл однородные элементы, их число меняется запись: каждая позиция — своё поле, число полей фиксировано
Пример в магазине товары в корзине позиция заказа (product_id, quantity, price)
Ключ словаря нельзя можно, если внутри только неизменяемые значения

Таблица 2. Варианты распаковки на кортеже item = (42, 2, 1499) и order = (1042, "paid", "мышь", "коврик").

Запись Результат Комментарий
pid, qty, price = item 42, 2, 1499 число переменных = числу элементов
pid, qty = item ValueError элементов больше, чем переменных
order_id, status, *products = order products == ["мышь", "коврик"] * собирает остаток в список
order_id, *_ = order order_id == 1042 _ — «это значение не нужно»
a, b = b, a значения поменялись местами обмен без временной переменной

На практике

Задача: хранить позиции заказа как записи (product_id, quantity, price), разбирать их на поля и считать итог заказа.

Шаг 1. Создаём кортеж

Позиция заказа — три поля в фиксированном порядке:

python
item = (42, 2, 1499)
print(item[0], item[-1], len(item))
print(type(item))

Результат:

вывод
42 1499 3
<class 'tuple'>

Шаг 2. Убеждаемся, что кортеж не меняется

Попробуем поменять количество в позиции:

python
item[1] = 3

Результат:

вывод
TypeError: 'tuple' object does not support item assignment

Методов, меняющих кортеж (append, remove), тоже нет: item.append(5) — AttributeError. «Изменить» кортеж — значит создать новый: item = (item[0], 3, item[2]).

Шаг 3. Распаковываем

Разбираем запись на поля одной строкой:

python
product_id, quantity, price = item
print(product_id, quantity, price)
print(quantity * price)

Результат:

вывод
42 2 1499
2998

Шаг 4. Следим за числом переменных

Если переменных меньше или больше, чем элементов, распаковка падает:

python
product_id, quantity = item

Результат:

вывод
ValueError: too many values to unpack (expected 2)

Шаг 5. Собираем остаток звёздочкой

Запись заказа: номер, статус, а дальше — сколько угодно товаров:

python
order = (1042, "paid", "мышь", "коврик")
order_id, status, *products = order
print(order_id, status, products)

Результат:

вывод
1042 paid ['мышь', 'коврик']

*products забирает всё, что осталось, и всегда становится списком — даже пустым.

Шаг 6. Создаём кортеж из одного элемента

Кортеж создают запятые, а не скобки. Без запятой скобки — просто скобки:

python
sizes = ("XL")
one_size = ("XL",)
print(type(sizes), type(one_size))

Результат:

вывод
<class 'str'> <class 'tuple'>

Шаг 7. Меняем значения местами

В акции «старая и новая цена» перепутали значения — меняем их распаковкой:

python
old_price, new_price = 990, 1499
old_price, new_price = new_price, old_price
print(old_price, new_price)

Результат:

вывод
1499 990

Справа сначала собирается кортеж (1499, 990), потом он распаковывается в переменные слева.

Шаг 8. Распаковываем в цикле

Заказ — список позиций-кортежей; распаковка прямо в заголовке for:

python
items = [(42, 2, 1499), (7, 1, 5990), (13, 3, 490)]
total = 0
for product_id, quantity, price in items:
    total += quantity * price
print(total)

Результат:

вывод
10458

Частые ошибки

  • ("XL") — не кортеж. Это строка в скобках. Кортеж из одного элемента — ("XL",). А "L" in ("XL") проверяет подстроку и даёт True.
  • Число переменных при распаковке. Не совпадает — ValueError: too many values to unpack или not enough values to unpack. Если длина может меняться — *rest.
  • Попытка изменить кортеж. item[1] = 3 — TypeError, item.append(...) — AttributeError. Создавайте новый кортеж.
  • «Неизменяемый» кортеж со списком внутри. (42, ["black"]) нельзя изменить по индексу, но список внутри меняется: item[1].append("white") работает. Подробно — в теме «Изменяемые и неизменяемые типы».

Итог

Кортеж

  • Упорядоченный и неизменяемый: индексы, срезы, len, in — как у списка; менять нельзя.
  • Подходит для записей с фиксированной структурой: (product_id, quantity, price).
  • Кортеж создают запятые: 42, 2, 1499; из одного элемента — ("XL",).

Распаковка

  • a, b, c = t — число переменных равно числу элементов.
  • first, *rest = t — остаток в список; _ — для ненужных значений.
  • a, b = b, a — обмен значениями.
  • Работает в заголовке for: for pid, qty, price in items:.

Вопросы на собеседовании

Зачем нужен кортеж, если есть список?

Кортеж неизменяем: его можно безопасно передавать и хранить, не боясь, что кто-то поменяет содержимое; он может быть ключом словаря и элементом множества (если внутри неизменяемые значения); он занимает немного меньше памяти. По смыслу кортеж — запись с фиксированными полями, список — коллекция однородных элементов.

Как создать кортеж из одного элемента?

С запятой: (42,) или просто 42,. (42) — это число 42 в скобках: кортеж создают запятые, а скобки только группируют.

Что выведет t = (1, 2, [3, 4]); t[2].append(5); print(t)?

(1, 2, [3, 4, 5]). Кортеж хранит ссылки на объекты и не даёт заменить их, но сам список внутри изменяемый — его содержимое меняется.

Глубже

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

Ошибка в тексте

Сообщить об ошибке могут зарегистрированные пользователи — так в сообщениях меньше спама.

Войти, чтобы сообщить