Python Unpacking
1Python Unpacking: overview
Unpacking assigns the items of any iterable to several names in one statement, and a * lets one name collect the leftovers or spread items into a list, dictionary or function call.
Before this lesson: Python Lists, Python Tuples
2Python Unpacking: diagram
3Python Unpacking: explained step by step
You met unpacking in the tuple lesson with width, height = size. It is far more general than tuples. Unpacking means spreading the items of any iterable (a list, a string, a range, the pairs of a dictionary) across several names in a single statement. The same idea also works in reverse, when you build a list, a dictionary or a function call out of existing pieces. This lesson goes from the basics to the patterns you will see in real code.
Any iterable, one name per item
The right side can be anything you could loop over. The left side needs one name for each item.
x, y, z = [10, 20, 30]
a, b, c = "cat"
lo, hi = range(5, 7)
year, month, day = "2025-03-14".split("-")
print(x, y, z)
print(a, b, c)
print(lo, hi)
print(year, month, day)The last line is the most common real use: split() returns a list, and unpacking gives every part a meaningful name instead of an index like parts[2].
When the counts do not match
If the number of names and the number of items differ, Python refuses with a ValueError that tells you which way you were off.
try:
a, b = [1, 2, 3]
except ValueError as error:
print(error)
try:
a, b, c = [1, 2]
except ValueError as error:
print(error)Swapping without a temporary
The right side is worked out completely first, packed into a tuple, and only then assigned to the names on the left. That is why a swap needs no helper variable, and why it can also update two values at once.
a, b = 1, 2
a, b = b, a
print(a, b)
x, y = 0, 1
for step in range(6):
x, y = y, x + y
print(x, y)Star targets: collect the rest
Put a * in front of one name and it soaks up whatever the other names leave over. The collected items always arrive as a list, even when you unpack a tuple or a string, and the list may be empty.
scores = [98, 91, 85, 77, 60]
best, *others = scores
print(best, others)
*earlier, latest = scores
print(earlier, latest)
first, *middle, last = scores
print(first, middle, last)
only, *rest = [42]
print(only, rest)You can use at most one starred name per assignment, and the plain names must still all get an item: a, *b, c = [1] fails because there is nothing for c.
Ignore what you do not need with _
By convention the name _ means "I don't need this value". It is a normal variable, so it works anywhere a name does. *_ throws away any number of items.
record = ("Asha", "Pune", 28, "asha@example.com")
name, _, age, _ = record
print(name, age)
name, *_, email = record
print(name, email)Nested unpacking
The left side can copy the shape of the data. Wrap the inner part in parentheses (or brackets) and Python unpacks the inside too.
trip = ("Pune", (18.52, 73.86))
city, (lat, lon) = trip
print(city, lat, lon)
(x1, y1), (x2, y2) = (1, 2), (4, 6)
print(x2 - x1, y2 - y1)Unpacking in for loops
The variable after for is just another assignment target, so everything above works there. Looping over a dictionary directly gives only its keys; call items() to get (key, value) pairs.
prices = {"tea": 3.5, "coffee": 4.0}
for drink, price in prices.items():
print(drink, price)
names = ["Asha", "Ben"]
scores = [88, 92]
for number, (name, score) in enumerate(zip(names, scores), start=1):
print(number, name, score)
rows = [("Asha", 80, 90), ("Ben", 70, 60, 80)]
for name, *marks in rows:
print(name, sum(marks) / len(marks))The middle loop is the pattern from the enumerate and zip lesson: each item is (number, (name, score)), so the inner pair gets its own parentheses. The last loop shows a star in a loop: rows of different lengths still work.
Unpacking into function calls: * and **
In a call, a single * spreads a sequence into separate positional arguments, and ** spreads a dictionary into keyword arguments. The dictionary keys must match the parameter names.
def book(origin, destination, nights=1):
return f"{origin} -> {destination}, {nights} night(s)"
route = ("Pune", "Goa")
options = {"nights": 3}
print(book(*route))
print(book(*route, **options))
print(*route, sep=" | ")
print(list(range(*(2, 10, 4))))Merging with * and **
Inside square brackets * pours the items of another iterable into the new list, and inside curly braces ** pours in key-value pairs. If a key appears twice, the later one wins. The result is always a new object.
weekdays = ["Mon", "Tue"]
weekend = ["Sat", "Sun"]
print([*weekdays, "Wed", *weekend])
defaults = {"theme": "light", "font": 12}
user = {"font": 14}
print({**defaults, **user})
print({**defaults, "theme": "dark"})A star has a different job on each side of the =: on the left it collects leftovers into a list, while in a call or a bracket it spreads items out. (A * in a function's def line, as in def f(*args), collects too. The args and kwargs lesson covers it.)
Quick reference: Python Unpacking
- How to Merge Two Dictionaries in Python — Use `merged = a | b` (Python 3.9+) or `merged = {**a, **b}` to merge two dictionaries into a new one. If both…
- How to Convert a Tuple to a List in Python — Use `list(my_tuple)`, for example `list((1, 2, 3))` returns `[1, 2, 3]`. The unpacking form `[*my_tuple]` doe…
- Python divmod() Function — `divmod(a, b)` returns the tuple `(a // b, a % b)`: the floor quotient and the remainder from a single divisi…
- Python String partition() Method — `str.partition(sep)` splits a string at the **first** occurrence of `sep` and always returns a tuple of three…
4Python Unpacking: syntax
a, b = [1, 2] # one name per item
first, *rest = items # rest is always a list
*start, last = items # star can come first, middle or last
first, *_, last = items # _ = a value you ignore
(x, y), z = (1, 2), 3 # nested pattern
a, b = b, a # swap
for key, value in d.items(): # unpack in a loop
func(*args, **kwargs) # spread into a call
new_list = [*a, *b] # merge lists
new_dict = {**a, **b} # merge dicts (later wins)
5Python Unpacking: run the example
Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.
line = "Asha,Pune,82,91,77"
name, city, *marks = line.split(",")
marks = [int(m) for m in marks]
best, *rest = sorted(marks, reverse=True)
print(name, "from", city)
print("Best mark:", best, "| others:", rest)
profile = {"name": name, "city": city}
defaults = {"city": "unknown", "active": True}
merged = {**defaults, **profile}
print(merged)
def greet(name, city):
return f"Hello {name} from {city}!"
print(greet(**profile))Expected outputAsha from Pune
Best mark: 91 | others: [82, 77]
{'city': 'Pune', 'active': True, 'name': 'Asha'}
Hello Asha from Pune!
7Python Unpacking: how the example works
Hover or tap a step to highlight its lines in the example above.
- Line 1A line of comma-separated text: a name, a city and three marks.
- Line 2
split(",")returns a list of five strings.nameandcitytake the first two, and*markscollects the remaining three as a list:['82', '91', '77']. - Line 3The marks are still text, so a list comprehension turns each one into an
int. - Line 4
sorted(..., reverse=True)gives[91, 82, 77]. The first value goes tobestand*restcollects[82, 77]. - Line 5-6Printing shows the pieces:
Asha from Puneand the best mark with the others. - Line 8A small dictionary built from the variables we just unpacked.
- Line 9-10
{**defaults, **profile}pours both dictionaries into a new one.profilecomes last, so its"city"replaces the default and"name"is added. The original dictionaries are not changed. - Line 11The merged dictionary keeps the key order of
defaultsfirst, then any new keys:city,active,name. - Line 13-14A function with two parameters named
nameandcity. - Line 15
greet(**profile)turns each key into a keyword argument, so it is the same asgreet(name="Asha", city="Pune"). This only works because the keys match the parameter names.
8Python Unpacking: common mistakes
Too many (or too few) names for the items
✗ Wrong
point = (3, 5, 7)
x, y = point✓ Right
point = (3, 5, 7)
x, y, z = point
print(x, y, z)
x, y, *_ = point
print(x, y)Every item needs a name. Add the missing name, collect the extra items with a star, or slice first.
Looping over a dictionary without items()
✗ Wrong
stock = {"pear": 3, "plum": 7}
for fruit, count in stock:
print(fruit, count)✓ Right
stock = {"pear": 3, "plum": 7}
for fruit, count in stock.items():
print(fruit, count)A loop over a dictionary produces its keys. Python tries to unpack the string "pear" (four characters) into two names. Use stock.items() to get (key, value) pairs.
Using two starred names
✗ Wrong
*head, *tail = [1, 2, 3, 4]✓ Right
head, *tail = [1, 2, 3, 4]
print(head, tail)Python could not tell how many items each star should take, so only one starred name is allowed. Use one star with plain names around it, or slice the list.
Spreading a dictionary whose keys do not match the parameters
✗ Wrong
def greet(name, city):
return f"Hello {name} from {city}!"
profile = {"name": "Asha", "town": "Pune"}
print(greet(**profile))✓ Right
def greet(name, city):
return f"Hello {name} from {city}!"
profile = {"name": "Asha", "city": "Pune"}
print(greet(**profile))** turns each key into a keyword argument, so the keys must be exactly the parameter names. Rename the key (or build a new dictionary with the right names).
Swapping with two separate assignments
✗ Wrong
a = 1
b = 2
a = b
b = a
print(a, b)✓ Right
a = 1
b = 2
a, b = b, a
print(a, b)After a = b both names hold 2, so there is nothing left to copy into b. Use one tuple assignment, which works out the right side before assigning anything.
9Python Unpacking: exercises
Write your answer, then press Check. The checks run in your browser.
1. Unpack a record
Write code Easy ✓ CompletedWrite three statements, each using unpacking:
- Unpack the list
recordinto the namesname,cityandage. - Unpack
date.split("-")intoyear,monthandday. lowandhighwere typed the wrong way round. Swap them with one statement, so thatlowis 3 andhighis 9.
Finally print name and year.
record = ["Asha", "Pune", 28]
date = "2025-03-14"
low, high = 9, 3
# your code here
record = ["Asha", "Pune", 28]
date = "2025-03-14"
low, high = 9, 3
name, city, age = record
year, month, day = date.split("-")
low, high = high, low
print(name, year)
2. Predict the unpacking
Predict the output Medium ✓ CompletedRead the code and type exactly what it will print, line by line. Remember that a starred name always collects its items into a list.
a, *b = "hello"
print(a, b)
x, (y, z), *w = 1, (2, 3), 4, 5
print(x, y, z, w)
first, *_, last = range(5)
print(first, last)
p, q = 1, 2
p, q = q, p + q
print(p, q)
h ['e', 'l', 'l', 'o']
1 2 3 [4, 5]
0 4
2 3
2 more exercises → 6 more examples
Questions learners ask
What is the difference between * in first, *rest = items and * in def f(*args)?
Both collect extra items into one name. In an assignment, the starred name collects leftover items into a list. In a function definition, *args collects extra positional arguments into a tuple. In a function call or inside brackets, * does the opposite and spreads a sequence out into separate items.
Why is my starred variable a list even when I unpack a tuple or a string?
A starred target always receives a new list, whatever kind of iterable you unpack. If nothing is left over it is an empty list, not None. Convert it with tuple(rest) or "".join(rest) if you need another type.
What does the underscore _ mean in a, _ = pair and in for _ in range(3)?
Python gives _ no special meaning. It is an ordinary variable name that programmers use for a value they do not intend to use. Using it tells readers that the value is deliberately ignored. Use *_ to ignore any number of items.
Further reading
10Next lesson
Up nextPython SortingSorting puts items in order: sorted() returns a new sorted list and leaves the original alone, list.sort() rearranges t…Related:
Python enumerate() and zip()Python TuplesPython DictionariesPython *args and **kwargs
← Python Tuples · All Data Structures lessons · Practise in the IDE