Tuple Unpacking and Multiple Assignment
Unpacking pulls a sequence apart into names in one statement. It powers swapping, loops over pairs, multiple return values and the star syntax.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
Packing and unpacking
packed = 3, 7, 9 # packing: several values into one tuple
a, b, c = packed # unpacking: one tuple into several names
print(packed) # (3, 7, 9)
print(a, b, c) # 3 7 9The number of names must match the number of values exactly, or Python raises an error that says precisely what went wrong.
values = (1, 2, 3)
# a, b = values # ValueError: too many values to unpack (expected 2)
# a, b, c, d = values # ValueError: not enough values to unpack (expected 4, got 3)Unpacking works on any iterable
a, b = [1, 2] # a list
x, y = "hi" # a string
p, q = {"first": 1, "second": 2} # a dict gives its KEYS
m, n = range(2)
print(a, b, x, y, p, q, m, n) # 1 2 h i first second 0 1Swapping
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1
x, y, z = 1, 2, 3
x, y, z = z, x, y # rotate in one statement
print(x, y, z) # 3 1 2The whole right hand side is evaluated into a tuple before any name is assigned, which is why no temporary variable is needed and why rotation works.
Star unpacking
first, *rest = [1, 2, 3, 4, 5]
print(first, rest) # 1 [2, 3, 4, 5]
*most, last = [1, 2, 3, 4, 5]
print(most, last) # [1, 2, 3, 4] 5
head, *middle, tail = [1, 2, 3, 4, 5]
print(head, middle, tail) # 1 [2, 3, 4] 5
a, *b = [1]
print(a, b) # 1 [] - the starred name can collect nothing- The starred name always collects a list, even when unpacking a tuple.
- Only one starred name is allowed per assignment.
- It may collect zero items without error.
line = "meera:engineer:pune:2019"
name, role, *extra = line.split(":")
print(name, role, extra) # meera engineer ['pune', '2019']Unpacking in loops
pairs = [("Meera", 92), ("Arun", 78)]
for name, score in pairs:
print(f"{name}: {score}")
ages = {"Meera": 27, "Arun": 31}
for name, age in ages.items():
print(name, age)
for index, (name, score) in enumerate(pairs, start=1):
print(index, name, score)
records = [("Meera", 92, "A"), ("Arun", 78, "B")]
for name, *details in records:
print(name, details) # Meera [92, 'A']Ignoring values
record = ("Meera", 27, "Pune", "engineer")
name, _, city, _ = record # underscore means "I do not need this"
print(name, city)
name, *_ = record # keep only the first
print(name)
for _ in range(3): # a loop that does not use the counter
print("tick")_ is an ordinary variable name by convention, not a language feature. It signals to a reader that the value is deliberately discarded.
Nested unpacking
data = ("Meera", (27, "Pune"))
name, (age, city) = data
print(name, age, city) # Meera 27 Pune
points = [(1, (2, 3)), (4, (5, 6))]
for x, (y, z) in points:
print(x, y, z)Unpacking into function calls
def area(width, height):
return width * height
dimensions = (4, 5)
print(area(*dimensions)) # 20 - the tuple is spread into arguments
def describe(name, role):
return f"{name} is a {role}"
record = {"name": "Meera", "role": "engineer"}
print(describe(**record)) # keyword arguments from a dictionaryBuilding collections with star
a = [1, 2]
b = [3, 4]
print([*a, *b]) # [1, 2, 3, 4]
print((*a, *b)) # (1, 2, 3, 4)
print({*a, *b}) # {1, 2, 3, 4}
first = {"a": 1}
second = {"b": 2}
print({**first, **second}) # {'a': 1, 'b': 2}Multiple return values
def split_name(full_name):
parts = full_name.split()
return parts[0], parts[-1]
first, last = split_name("Meera Sunita Nair")
print(first, last) # Meera Nair
quotient, remainder = divmod(17, 5)
key, sep, value = "timeout=30".partition("=")
print(quotient, remainder, key, value)A worked example
rows = [
"Meera,engineer,90000",
"Arun,designer,75000",
"Sara,analyst,82000",
]
total = 0
for row in rows:
name, role, salary = row.split(",")
salary = int(salary)
total += salary
print(f"{name:<8}{role:<12}{salary:>8,}")
print(f"{'Total':<20}{total:>8,}")Common mistakes
- Mismatching the number of names and values.
- Forgetting that a starred name produces a list, not a tuple.
- Using two starred names in one assignment.
- Unpacking a dictionary and expecting values; you get keys unless you use
.items(). - Reusing
_for something you then need. - Unpacking a generator twice; it is exhausted after the first pass.
Best practices
- Unpack in the
forstatement rather than indexing inside the loop. - Use
_for values you deliberately discard, so reviewers know it was intentional. - Use star unpacking for "the first one and the rest" rather than slicing twice.
- Prefer
*argsspreading over building argument lists by hand. - Keep nested unpacking to one level; deeper is hard to read.
Practice
- Swap three variables cyclically in a single statement.
- Split
"2026-08-22T14:30:00"into date parts and time parts using unpacking only. - Write a function returning four values and unpack only the first and last.
- Explain why
a, *b, *c = [1, 2, 3]is a syntax error. - Merge three dictionaries into one using star unpacking, and say which wins on a duplicate key.
Conclusion
Unpacking turns a sequence into named values in one line. Combined with the star syntax it handles variable length data, and combined with for it removes almost all indexing from loops over pairs and records.