Целевой ссылкой в операции присваивания может служить список, состоящий из двух и более ссылок, разделенных запятыми, которые могут заключаться в необязательные круглые или квадратные скобки.
a, b, c = iterable
Эта инструкция, в которой требуется, чтобы объект iterable
был итерируемым и содержал ровно три элемента, связывает а
с первым элементом, b
- со вторым и c
- с третьим. Операции такого рода носят название присваивание с распаковкой. Выражение в правой части должно быть итерируемым и содержать ровно столько элементов, сколько указано ссылок в левой части, в противном случае Python бросает исключение. Каждая из ссылок, указанных в левой части, связывается с соответствующим элементом из правой части.
Присваивание с распаковкой можно использовать для обмена значениями переменных:
a, b = b, a
Данная инструкция присваивания повторно связывает имя a
со значением, с которым было связано имя b
, и наоборот.
Если имеется несколько целевых ссылок, то разрешается помечать одну из них символом *
- звездочка. Целевая ссылка, которой предшествует символ *
, связывается со списком всех элементов, оставшихся не связанными с другими целевыми ссылками.
first, *middle, last = iterable # что эквивалентно записи без '*' звездочки (расширенной распаковки) first, middle, last = iterable[0], iterable[1:-1], iterable[-1]
В обеих инструкциях присваивания требуется, чтобы объект iterable
имел по крайней мере два элемента. Эту форму операции присваивания называют расширенной распаковкой.
Глубокая или расширенная распаковка, в некотором смысле похожа на множественное присваивание. Множественное присваивание позволяет сопоставить длину итерации в правой части присваивания и получить каждый элемент в переменной. Подобным образом глубокая распаковка позволяет сопоставить форму того, что находится в правой части задания.
Например, используя множественное присваивание дважды, можно сделать следующее:
>>> colour_info = ("AliceBlue", (240, 248, 255)) >>> name, rgb_values = colour_info >>> name # 'AliceBlue' >>> r, g, b = rgb_values >>> g # 248
Но если необходимо получить отдельные значения RGB, то можно сразу использовать глубокую распаковку:
>>> colour_info = ("AliceBlue", (240, 248, 255)) >>> name, (r, g, b) = colour_info >>> name # 'AliceBlue' >>> g # 248
Обратите внимание, что переменные r
, g
и b
группируются с помощью скобок ()
, для создания кортежа, имитирующего форму переменной colour_info
. Если просто написать name, r, g, b = colour_info
, то Python будет пытаться выполнить четыре присваивания, и ожидал бы от переменной colour_info
последовательность из 4-х элементов:
>>> colour_info = ("AliceBlue", (240, 248, 255)) >>> name, r, g, b = colour_info # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # ValueError: not enough values to unpack (expected 4, got 2)
Глубокая/расширенная распаковка также может использоваться в неявном назначении циклов for/in
, она не обязательно должна быть в явном виде со знаком равенства!
Глубокая/расширенная распаковка при правильном использовании может улучшить читаемость вашего кода - убрав беспорядок при извлечении значений по индексу, а также может помочь протестировать код на наличие некоторых ошибок.
Например:
def greyscale(colour_info): # для вычисления оттенка, функция использует срезы return 0.2126*colour_info[1][0] + 0.7152*colour_info[1][1] + 0.0722*colour_info[1][2] colours = [ # !!!Ошибка в задании цвета `AliceBlue`!!! ("AliceBlue", (240, 248, 255, 127, 255, 212)), ("DarkCyan", (0, 139, 139)), ] # !!!функция отработала без ошибок!!! print(greyscale(colours[0])) # 246.8046
Однако, если написать функцию, которая использует глубокую/расширенную распаковку, то можно поймать ошибку:
def greyscale(colour_info): # код вычисления оттенка более лаконичный и короткий name, (r, g, b) = colour_info return 0.2126*r + 0.7152*g + 0.0722*b colours = [ # !!!Ошибка в задании цвета `AliceBlue`!!! ("AliceBlue", (240, 248, 255, 127, 255, 212)), ("DarkCyan", (0, 139, 139)), ] # вот и ошибка print(greyscale(colours[0])) # ValueError: too many values to unpack (expected 3)
>>> a, b, c = 0, 5, 10 >>> a, b, c # 0 5 10 >>> a, b, c = ['one', 'two', 'three'] >>> a, b, c # one two three >>> a, b = b, a >>> a, b # two one >>> a, *b = ['one', 'two', 'three'] >>> a, b # one ['two', 'three']
Пример реализации функции, похожей на функцию reduce
из модуля functools
при помощи распаковки последовательности.
def reduce(function, list_): """Уменьшение списка с помощью функции.""" if not list_: raise TypeError("Cannot reduce empty list.") value, *list_ = list_ while list_: val, *list_ = list_ value = function(value, val) return value
>>> fruits = ['lemon', 'orange', 'banana', 'tomato'] >>> first, second, *orher = fruits >>> orher # ['banana', 'tomato'] >>> first, *orher = fruits >>> orher # ['orange', 'banana', 'tomato'] >>> first, *middle, last = fruits >>> middle # ['orange', 'banana']
>>> fruits = ['lemon', 'orange', 'banana', 'tomato'] >>> ((first_letter, *other_letter), *other) = fruits >>> first_letter, other_letter # l ['e', 'm', 'o', 'n'] >>> other # ['orange', 'banana', 'tomato']
Теперь представим, что есть список с несколькими цветами и нужно вычислить оттенки серого. Для этого можно использовать глубокую распаковку в цикле for/in
(а также используя генератор списка):
colours = [ ("AliceBlue", (240, 248, 255)), ("Aquamarine", (127, 255, 212)), ("DarkCyan", (0, 139, 139)), ] greyscales = [ round(0.2126*r + 0.7152*g + 0.0722*b, 2) for name, (r, g, b) in colours ] print(greyscales) # [246.8, 224.68, 109.45]
>>> fruits = ['lemon', 'orange', 'banana', 'tomato'] >>> x = [*fruits, *reversed(fruits)] >>> x # ['lemon', 'orange', 'banana', 'tomato', 'tomato', 'banana', 'orange', 'lemon'] >>>x = [*fruits[2:], *fruits[:2]] >>> x # ['banana', 'tomato', 'lemon', 'orange'] # Здесь создадим кортеж >>> x = (*fruits[1:], fruits[0]) >>> x # ('orange', 'banana', 'tomato', 'lemon')