Распаковка (unpacking, также называемая Деструктуризация) представляет разложение коллекции (кортежа, списка и т.д.) на отдельные значения.
Так, как и многие языки программирования, Python поддерживает концепцию множественного присваивания. Например:
x, y = 1, 2 print(x) # 1 print(y) # 2
В данном случае присваивем значения сразу двум переменным. Присвоение идет по позиции: переменная x получает значение 1, а переменная y - значени 2.
Данный пример в действительности уже представляет деструктуризацию или распаковку. Значения 1, 2
фактически являются кортежом, поскольку именно запятые между значениями
говорят о том, что это кортеж. И мы также могли бы написать следующим образом:
x, y = (1, 2) print(x) # 1 print(y) # 2
В любом случае мы имеем дело с деструктуризацией, когда первый элемент кортежа передается первой переменной, второй элемент - второй переменной и так далее. То есть разложение идет по позиции.
Подобным образом можно разложить другие кортежи, например:
name, age, company = ("Tom", 38, "Google") print(name) # Tom print(age) # 38 print(company) # Google
Только кортежами мы не ограничены и можем "распаковывать" и другие коллекции, например, списки:
people = ["Tom", "Bob", "Sam"] first, second, third = people print(first) # Tom print(second) # Bob print(third) # Sam
При разложении словаря переменные получают ключи словаря:
dictionary = {"red": "красный", "blue": "синий", "green": "зеленый"} r, b, g = dictionary print(r) # red print(b) # blue print(g) # green # получаем значение по ключу print(dictionary[g]) # зеленый
Циклы в Python позволяют разложить коллекции на отдельные составляющие:
people = [ ("Tom", 38, "Google"), ("Bob", 42, "Microsoft"), ("Sam", 29, "JetBrains") ] for name, age, company in people: print(f"Name: {name}, Age: {age}, Company: {company}")
Здесь мы перебираем список кортежей people. Каждый кортеж состоит из трех элементов, соответственно при переборе мы можем их передать в переменные name, age и company.
Другой пример - функция enumerate(). Она принимает в качестве параметра коллекцию, создает для каждого элемента кортеж и возвращает набор из подобных кортежей. Каждый кортеж содержит индекс, который увеличивается с каждой итерацией:
people = ["Tom", "Bob", "Sam"] for index, name in enumerate(people): print(f"{index}.{name}") # результат # 0.Tom # 1.Bob # 2.Sam
Если какой-то элемент коллекции не нужен, то обычно для него определяется переменная с именем _ (прочерк):
person =("Tom", 38, "Google") name, _, company = person print(name) # Tom print(company) # Google
Здесь нам не важен второй элемент кортежа, поэтому для него определяем переменную _. Хотя в реальности _ - такое же действительное имя, как name и company:
name, _, company = person print(_) # 38
Оператор * упаковывает значение в коллекцию. Например:
num1=1 num2=2 num3=3 *numbers,=num1,num2,num3 print(numbers) #[1, 2, 3]
Здесь мы упаковываем значения из кортежа (num1,num2,num3) в список numbers. Причем, чтобы получить список, после numbers указывается запятая.
Как правило, упаковка применяется для сбора значений, которые остались после присвоения результатов деструктуризации. Например:
head, *tail = [1, 2, 3, 4, 5] print(head) # 1 print(tail) # [2, 3, 4, 5]
Здесь переменная head
в соответствии с позицией получае первый элемент списка. Все остальные элементы передаются в переменную tail
. Таким образом,
переменная tail
будет представлять список из оставшихся элементов.
Аналогичным образом можно получить все кроме последнего:
*head, tail = [1, 2, 3, 4, 5] print(head) # [1, 2, 3, 4] print(tail) # 5
Или элементы по середине, кроме первого и последнего:
head, *middle, tail = [1, 2, 3, 4, 5] print(head) # 1 print(middle) # [2, 3, 4] print(tail) # 5
Или все кроме первого и второго:
first, second, *other = [1, 2, 3, 4, 5] print(first) # 1 print(second) # 2 print(other) # [3, 4, 5]
Вообщем, таким образом мы можем получать различные комбинации элементов коллекции. Причем не только списков, но и кортежей, словарей и других коллекций.
Другой пример - нам надо получить только первый, третий и последний элемент, а остальные элементы нам не нужны. В общем случае мы должны предоставить переменные для всех элементов коллекции. Однако если коллекция имеет 100 элементов, а нам нужно только три, не будем же мы определять все сто переменных. И в этом случае опять же можно применить упаковку:
first, _, third, *_, last = [1, 2, 3, 4, 5, 6, 7, 8] print(first) # 1 print(third) # 3 print(last) # 8
Также можно получить ключи словаря:
red, *other, green = {"red":"красный", "blue":"синий", "yellow":"желтый", "green":"зеленый"} print(red) # red print(green) # green print(other) # ['blue', 'yellow']
Оператор * вместе с оператором ** также может применяться для распаковки значений. Оператор * используется для распаковки кортежей, списков, строк, множеств, а оператор ** - для распаковки словарей. Особенно это может быть полезно, когда на основе одних коллекций создаются другие. Например, распаковка кортежей и списков:
nums1 = [1, 2, 3] nums2 = (4, 5, 6) # распаковываем список nums1 и кортеж nums2 nums3 = [*nums1, *nums2] print(nums3) # [1, 2, 3, 4, 5, 6]
Здесь распаковывем значения из списка nums1 и кортежа nums2 и помещаем их в список nums3.
Подобным образом раскладываются словари, только применяется оператор **:
dictionary1 = {"red":"красный", "blue":"синий"} dictionary2 = {"green":"зеленый", "yellow":"желтый"} # распаковываем словари dictionary3 = {**dictionary1, **dictionary2} print(dictionary3) # {'red': 'красный', 'blue': 'синий', 'green': 'зеленый', 'yellow': 'желтый'}