Упаковка и распаковка

Последнее обновление: 01.02.2023

Распаковка

Распаковка (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': 'желтый'}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850