Перейти к содержимому
Теория · Кортежи и распаковка · урок 3 из 8

Распаковка

Представьте две чашки: в левой чай, в правой кофе. Нужно поменять напитки местами. Если сразу перелить кофе в левую чашку, чай пропадёт. Без третьей, пустой чашки не обойтись.

С переменными то же самое. Двух строк a = b и b = a не хватает: после первой старое значение a потеряно, и обе переменные ссылаются на одно число. В Python обмен записывают одной строкой:

Python
a = 1
b = 2
a, b = b, a
print(a, b)  # 2 1

Роль третьей чашки здесь играет кортеж. Давайте посмотрим, как он появляется и куда потом исчезает.

Как работает распаковка

Вспомните запаянную упаковку из прошлого урока. Строка a, b = b, a сначала собирает такую упаковку, а потом вскрывает её. Шагов два:

  1. Справа от = стоит кортеж b, a. Python вычисляет его целиком из текущих значений и получает (2, 1).
  2. Затем распаковка раскладывает элементы кортежа по именам слева по порядку: первый — в a, второй — в b.

Пока правая часть не вычислена полностью, ни одно имя слева не меняется. Поэтому старое значение a не теряется: оно уже лежит в упаковке, когда a получает новое.

Нажимайте «Следующий шаг» и следите за переменными. На каждой строке с распаковкой они меняются только после того, как справа готов весь кортеж.

ВживуюРаспаковка: сначала кортеж, потом именашаг 1 из 12 · рамок: 1

Справа от = вызов divide(17, 5). Имён q и r ещё нет.

Имена слева меняются, только когда справа готов весь кортеж

Тот же порядок объясняет и возврат нескольких значений из функции:

Python
def divide(a, b):
    return a // b, a % b


q, r = divide(17, 5)
print(q, r)  # 3 2
a, b = 1, 2
a, b = b, a + b
print(a, b)  # 2 3

Давайте разберём пример по строкам:

  1. divide(17, 5) возвращает один объект — кортеж (3, 2).
  2. q, r = … сразу раскладывает его: q получает 3, а r получает 2.
  3. В строке a, b = b, a + b выражение a + b считается по старому a, равному 1.
  4. Поэтому b получает 3, а не 4, и программа печатает 2 3.

Справа может стоять любая последовательность: x, y, z = [10, 20, 30] распаковывает список, а first, second = "ok" — строку.

На собеседовании часто просят поменять местами две переменные без третьей. Ответ — a, b = b, a, и важно объяснить, почему он работает: правая часть вычисляется целиком до первого присваивания.

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

Имя со звёздочкой

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

Python
first, *rest = [5, 6, 7, 8]
print(first, rest)  # 5 [6, 7, 8]
head, *middle, tail = (1, 2, 3, 4)
print(head, middle, tail)  # 1 [2, 3] 4
x, *others = [9]
print(x, others)  # 9 []

Что делает каждая строка:

  • first, *rest — first берёт первый элемент, а rest — все остальные;
  • head, *middle, tail — крайние элементы получают обычные имена, а середина уходит в middle;
  • x, *others = [9] — лишних элементов нет, и others получает пустой список.

Так первый элемент отделяют от остальных, когда длина последовательности заранее неизвестна. Например, cmd, *files = "copy a.txt b.txt".split() кладёт команду в cmd, а имена файлов — в список files.

Имя со звёздочкой всегда получает список, даже если справа кортеж. Звёздочка может стоять у первого, среднего или последнего имени, но только у одного. При двух звёздочках Python не знает, как поделить элементы между ними, и сообщает об ошибке SyntaxError.

Частая ошибка

Число имён слева не совпало с числом элементов справа:

Python
point = (3, 4, 5)
x, y = point  # ✗
TEXT
ValueError: too many values to unpack (expected 2)

Без звёздочки имён должно быть ровно столько, сколько элементов. Число в expected 2 — это количество имён слева. Когда имён больше, сообщение другое: ValueError: not enough values to unpack (expected 3, got 2).

Исправить можно третьим именем: x, y, z = point. Если остальные элементы не нужны, подойдёт звёздочка: x, y, *rest = point.

Главное

  • При распаковке Python сначала вычисляет всю правую часть, а потом раскладывает элементы по именам слева направо.
  • Поэтому a, b = b, a меняет значения местами, а return q, r возвращает один кортеж.
  • Имя со звёздочкой собирает лишние элементы в список, возможно пустой.
  • Без звёздочки число имён должно совпадать с числом элементов, иначе будет ValueError.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом