Перейти к содержимому
Editor

Распаковка в Python: звёздочка, обмен значений, деструктуризация

Средний20 min5 упр.70 XP

Этот урок переведён с английского оригинала.Read in EnglishLeer en españolСообщить об ошибке в переводе

0/5 упр.

Представьте, что вы заказали посылку, в которой три вещи: книга, наушники и чехол для телефона. Когда коробка приходит, вы открываете её и раскладываете всё по местам. Книга отправляется на полку, наушники на стол, а чехол на телефон.

Распаковка в Python работает так же. Вы берёте набор значений (кортеж, список или любой итерируемый объект) и одной аккуратной строкой присваиваете каждое значение своей переменной. Не нужно доставать элементы по одному через индексы.

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

Как работает базовая распаковка?

Если у вас есть кортеж или список из нескольких значений, каждое из них можно присвоить отдельной переменной одной строкой. Число переменных слева должно совпадать с числом элементов справа.

Базовая распаковка кортежей и списков
# Unpacking a tuple
coordinates = (10, 20, 30)
x, y, z = coordinates
print(x) # 10
print(y) # 20
print(z) # 30
# Unpacking a list
colors = ['red', 'green', 'blue']
first, second, third = colors
print(first) # red
print(second) # green
print(third) # blue
Вывод
10
20
30
red
green
blue

Без распаковки пришлось бы обращаться к каждому элементу по индексу. С распаковкой код становится короче и читается гораздо легче.

Без распаковки (многословно)
point = (10, 20, 30)
x = point[0]
y = point[1]
z = point[2]
print(x, y, z)
С распаковкой (аккуратно)
point = (10, 20, 30)
x, y, z = point
print(x, y, z)

Распаковывать можно и прямо в присваивании, не создавая сначала отдельный кортеж в переменной. Когда вы перечисляете значения через запятую, Python сам собирает из них кортеж.

Распаковка без скобок
# Direct unpacking (no parentheses needed)
name, age, city = 'Alice', 25, 'Paris'
print(name) # Alice
print(age) # 25
print(city) # Paris
# This works because Python sees commas and creates a tuple
print(type(('Alice', 25, 'Paris'))) # <class 'tuple'>
Вывод
Alice
25
Paris
<class 'tuple'>

Как поменять значения переменных местами одной строкой

В большинстве языков программирования, чтобы поменять местами значения двух переменных, нужна временная переменная. Благодаря распаковке в Python она не нужна. Обмен значений умещается в одну изящную строку.

Обмен значений двух переменных
a = 'hello'
b = 'world'
# Swap in one line!
a, b = b, a
print(a) # world
print(b) # hello
Вывод
world
hello

Это работает, потому что Python сначала вычисляет правую часть и упаковывает значения в кортеж (b, a), а затем распаковывает его в a, b. Всё происходит за один безопасный шаг.

Другие языки (нужна временная переменная)
a = 1
b = 2
temp = a
a = b
b = temp
print(a, b)  # 2 1
Python (одна строка)
a = 1
b = 2
a, b = b, a
print(a, b)  # 2 1

Так же можно переставить значения трёх и более переменных. Python справится с этим за один шаг.

Циклический сдвиг трёх переменных
# Rotate three variables
a, b, c = 1, 2, 3
print(f'Before: a={a}, b={b}, c={c}')
a, b, c = b, c, a
print(f'After: a={a}, b={b}, c={c}')
Вывод
Before: a=1, b=2, c=3
After:  a=2, b=3, c=1

Что такое выражения со звёздочкой (*rest)?

Иногда вы не знаете точно, сколько элементов в коллекции, или вам нужны лишь несколько конкретных. Выражение со звёздочкой (*variable) собирает все оставшиеся элементы в список.

Выражения со звёздочкой собирают оставшиеся элементы
# Capture the first item and everything else
numbers = [1, 2, 3, 4, 5]
first, *rest = numbers
print(first) # 1
print(rest) # [2, 3, 4, 5]
# Capture the last item and everything before it
*beginning, last = numbers
print(beginning) # [1, 2, 3, 4]
print(last) # 5
Вывод
1
[2, 3, 4, 5]
[1, 2, 3, 4]
5

Звёздочку можно поставить и в середину. Это удобно, когда вам нужны первый и последний элементы, а всё, что между ними, можно пропустить.

Звёздочка в середине
scores = [95, 80, 75, 90, 85, 70, 88]
# Get the first, last, and everything in between
first, *middle, last = scores
print(f'First: {first}') # 95
print(f'Middle: {middle}') # [80, 75, 90, 85, 70]
print(f'Last: {last}') # 88
Вывод
First: 95
Middle: [80, 75, 90, 85, 70]
Last: 88
Звёздочка без элементов и со строками
# Star captures zero items (still a list)
a, b, *rest = [10, 20]
print(a) # 10
print(b) # 20
print(rest) # []
# Star from a string
first, *middle, last = 'Hello'
print(first) # H
print(middle) # ['e', 'l', 'l']
print(last) # o
Вывод
10
20
[]
H
['e', 'l', 'l']
o

Как использовать распаковку в циклах for

Особенно хорошо распаковка показывает себя в циклах for. Когда вы перебираете список кортежей (или списков), каждый элемент можно сразу распаковать в именованные переменные, а не обращаться к ним по индексу.

Распаковка кортежей в цикле for
# List of (name, age) tuples
students = [
('Alice', 16),
('Bob', 15),
('Charlie', 17),
]
# Unpack each tuple in the loop
for name, age in students:
print(f'{name} is {age} years old')
Вывод
Alice is 16 years old
Bob is 15 years old
Charlie is 17 years old

Этот приём постоянно применяют вместе с enumerate() и dict.items(). Обе функции возвращают кортежи, и распаковка делает код намного чище.

Распаковка с enumerate() и dict.items()
# enumerate() returns (index, value) tuples
fruits = ['apple', 'banana', 'cherry']
for i, fruit in enumerate(fruits):
print(f'{i}: {fruit}')
print()
# dict.items() returns (key, value) tuples
ages = {'Alice': 16, 'Bob': 15, 'Charlie': 17}
for name, age in ages.items():
print(f'{name} -> {age}')
Вывод
0: apple
1: banana
2: cherry

Alice -> 16
Bob -> 15
Charlie -> 17

Как распаковывать вложенные структуры данных

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

Вложенная распаковка со скобками
# A tuple containing a nested tuple
record = ('Alice', (95, 87, 92))
# Nested unpacking
name, (score1, score2, score3) = record
print(f'{name}: {score1}, {score2}, {score3}')
Вывод
Alice: 95, 87, 92

Это работает на любой глубине. Вложенную распаковку можно даже сочетать с выражениями со звёздочкой, чтобы получить максимум гибкости.

Вложенная распаковка в циклах
# List of students with (name, (math, science, english))
students = [
('Alice', (95, 87, 92)),
('Bob', (78, 85, 90)),
('Charlie', (88, 92, 76)),
]
for name, (math, science, english) in students:
avg = (math + science + english) / 3
print(f'{name}: average = {avg:.1f}')
Вывод
Alice: average = 91.3
Bob: average = 84.3
Charlie: average = 85.3

Частые ошибки при распаковке

Когда вы освоитесь, распаковка покажется интуитивной, но есть несколько ловушек, в которые часто попадают новички.

Частые ошибки при распаковке
# Mistake 1: Forgetting parentheses in nested unpacking
# Wrong: name, x, y = ('Alice', (10, 20)) -> ValueError!
# Right:
name, (x, y) = ('Alice', (10, 20))
print(name, x, y)
# Mistake 2: Unpacking a single-item tuple
# You need the comma to make it a tuple!
value, = (42,) # Note the comma after 42
print(value) # 42
# Mistake 3: Forgetting that * always creates a list
first, *rest = 'AB'
print(type(rest)) # <class 'list'>, not str!
Вывод
Alice 10 20
42
<class 'list'>

Упражнения для практики

Распакуйте координаты
Напишите код

Распакуйте кортеж point в три переменные: x, y и z. Затем выведите их в одну строку через пробел с помощью print(x, y, z).

point = (7, 14, 21)
# Unpack into x, y, z and print them
Поменяйте местами две переменные
Напишите код

В переменных first и second лежат не те значения. Поменяйте их местами с помощью распаковки (одной строкой, без временной переменной), а затем выведите обе переменные.

first = 'goodbye'
second = 'hello'
# Swap first and second in one line
print(first)
print(second)
Голова и хвост со звёздочкой
Напишите код

С помощью выражения со звёздочкой распакуйте список numbers в head (первый элемент) и tail (все остальные). Выведите обе переменные на отдельных строках.

numbers = [10, 20, 30, 40, 50]
# Unpack into head and tail using *
Предскажите результат распаковки
Угадайте вывод

Что выведет этот код? Подумайте, что соберёт звёздочка и каким типом это станет.

a, *b, c = [1, 2, 3, 4, 5, 6]
print(a)
print(b)
print(c)

Запустите код, чтобы проверить свой ответ.

# What does this print?
a, *b, c = [1, 2, 3, 4, 5, 6]
print(a)
print(b)
print(c)
Распакуйте записи об учениках
Напишите код

У вас есть список записей об учениках. Каждая запись представляет собой кортеж вида (name, (math_score, english_score)). С помощью вложенной распаковки в цикле for вычислите и выведите средний балл каждого ученика.

Выводите каждую строку в формате Name: average, где average округлено до одного знака после запятой с помощью :.1f.

students = [
('Alice', (90, 80)),
('Bob', (70, 90)),
('Charlie', (85, 95)),
]
# Loop with nested unpacking and print averages

Похожие уроки

Урок был полезен?