Кортежи и распаковка в Python: обмен и разбор значений

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

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

Кортеж создаёт запятая

Скобки помогают чтению, но решающей является запятая. Выражение point = 10, 20 создаёт пару, single = (10,) — одноэлементный кортеж, а (10) остаётся целым числом. Пустой кортеж записывается как ().

Неизменяемость относится к ссылкам в самом кортеже: нельзя присвоить point[0] = 5. Если элементом является список, содержимое списка остаётся изменяемым. Поэтому наличие кортежа само по себе не делает весь граф объектов неизменяемым или хешируемым.

Точное и расширенное присваивание

Слева должно быть столько целей, сколько значений справа. Звёздочная цель собирает оставшуюся середину в новый список:

first, second = (10, 20)
head, *middle, tail = [1, 2, 3, 4, 5]
left = "L"
right = "R"
left, right = right, left

print(first, second)
print(head, middle, tail)
print(left, right)

Обмен сначала вычисляет правый кортеж значений, а затем связывает цели. Ожидаемый вывод — 10 20, 1 [2, 3, 4] 5 и R L. Звёздочной может быть только одна цель на одном уровне распаковки.

Распаковка в цикле и при возврате

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

def score_range(scores):
    return min(scores), max(scores)


records = [
    ("Ada", 91, "active"),
    ("Lin", 84, "active"),
    ("Sam", 78, "paused"),
]

low, high = score_range([score for _, score, _ in records])
active_names = [name for name, _, status in records if status == "active"]

print(f"range={low}..{high}")
print(active_names)

Ожидаемый результат — range=78..91 и ['Ada', 'Lin']. Имя _ по соглашению отмечает неиспользуемое значение, но остаётся обычной переменной; повторное использование в одной распаковке просто перезаписывает её.

Когда выбрать другую структуру

Кортеж хорошо читается, пока позиционный смысл очевиден. Если запись содержит много полей или её формат развивается, выражение record[4] ничего не объясняет. Тогда именованный тип, dataclass или словарь может лучше передать назначение. Кортеж не должен становиться скрытой таблицей со множеством магических индексов.

Как найти несоответствие формы данных

ValueError: too many values to unpack и not enough values to unpack означают несоответствие фактической длины ожидаемой форме. Перед исправлением выведите repr(value), его тип и, если объект поддерживает длину, len(value). Частая ловушка — распаковать строку как запись: name, score = "Ada" разбирает символы, а не логические поля. Не добавляйте звёздочную цель автоматически: сначала решите, допустимо ли переменное число элементов по контракту данных.

Разбираем строки в кортежи с проверкой

Получите список строк вида город;минимум;максимум, разделите каждую строку и распакуйте три поля. Преобразуйте температуры в числа, найдите общую пару минимум–максимум и обменяйте её местами, чтобы увидеть семантику присваивания. Затем добавьте строку с лишним полем и напишите явную проверку длины с понятным сообщением. Самостоятельная проверка пройдена, если корректные записи обрабатываются, а испорченная не теряет поле молча.

Правила безопасной распаковки

Вопросы о кортежах и звёздочной цели

Почему *rest всегда даёт список, а не кортеж?

Так определена семантика расширенной распаковки: звёздочная цель получает новый список оставшихся элементов независимо от типа исходной последовательности.

Можно ли распаковывать генератор?

Да, распаковка работает с итерируемыми объектами, но для проверки количества ей придётся потребить значения. Для бесконечного итератора такая операция не завершится.

Чем возврат двух значений отличается от возврата кортежа?

На уровне объекта функция возвращает один кортеж, созданный запятыми. Запись low, high = function() сразу распаковывает этот единственный возвращённый объект.

Источники