PYTHON / LISTS AND TUPLES
Tuple unpacking and starred targets
Unpack any iterable into multiple variables, use a starred target to absorb a variable number of items, and read nested unpacking patterns.
What you will learn
- Bind several names at once from any iterable, matching counts exactly
- Use one starred target to collect leftover items as a list
- Place the star at the start, middle, or end and know which items it takes
- Read and write nested patterns like a, (b, c) = 1, (2, 3)
Understanding Tuple unpacking and starred targets
Writing several names on the left of `=` does not require a tuple on the right. Python takes whatever is on the right, iterates over it, and binds items to targets in order. That is why `a, b = "hi"` works and gives a = 'h', b = 'i': a string is iterable. If the number of items does not equal the number of targets, you get a ValueError, because Python has no rule for guessing which target to skip.
A starred target relaxes that exact-count rule to a minimum-count rule. Python counts the plain targets, hands each of them one item, and gives everything left over to the starred name. So in `first, *rest, last = xs` the source needs at least two items; `first` and `last` take one each and `rest` takes the middle, possibly zero items. The starred name is always bound to a list, even when the source is a tuple or a string, because the leftovers are collected into a fresh container rather than sliced out of the original.
Two more consequences follow from the fact that the right side is fully evaluated before any target is bound. First, `a, b = b, a` swaps cleanly: the pair `(b, a)` is built from the old values, then unpacked. Second, unpacking a one-shot iterator such as a generator or a file object consumes it, and a starred target will pull every remaining item into memory. Nesting works by matching shape, so `(a, b), c = (1, 2), 3` unpacks the inner pair as well.
point = (3, 7)
x, y = point
print(x, y)
first, *rest = [10, 20, 30, 40]
print(first, rest)
*head, last = "abcd"
print(head, last)
a, (b, c) = 1, (2, 3)
print(a, b, c)
x, y = y, x
print(x, y)Multi-target assignment iterates the right-hand side and binds items positionally, and a starred target absorbs the leftover items as a list.
Worked examples
Star in the middle, and empty stars
Shows that the starred target takes whatever is left after the plain targets, including nothing.
scores = [90, 80, 70]
first, *middle, last = scores
print(first, middle, last)
a, *b, c = [1, 2]
print(a, b, c)
for name, *marks in [("ann", 8, 9), ("bo", 7)]:
print(name, marks, sum(marks))Example explained
Line 1`first, *middle, last` needs at least 2 items; the two plain targets are served first, so middle gets [80].
Line 2With only two items nothing is left over, so b is [] rather than an error.
Line 3A for loop target list follows the same rules, so each row is split into a name plus a list of marks.
Line 4The rows have different lengths, which is exactly what the starred target absorbs.
What goes wrong when counts do not match
Compares the two ValueError messages Python raises for too many and too few items.
data = [1, 2, 3]
try:
a, b = data
except ValueError as e:
print("error:", e)
try:
a, b, c, d = data
except ValueError as e:
print("error:", e)
a, *b = data
print(a, b)Example explained
Line 1Two targets against three items raises immediately; Python will not silently drop the third.
Line 2Four targets against three items reports how many it actually found, which is useful when the source is data you did not write.
Line 3Adding a star turns the fixed count into a minimum, so the same list now unpacks without error.
Line 4Both failures are ValueError, not TypeError, because the object was iterable and only the length was wrong.
Splitting a record with a known head
Uses a starred target to keep the fixed leading fields separate from a variable number of trailing values.
line = "2024-05-01,sensor7,19.5,21.0,20.2"
date, sensor, *temps = line.split(",")
print(date, sensor, [float(t) for t in temps])
_, _, third = (1, 2, 3)
print(third)
((a, b), c) = ((1, 2), 3)
print(a + b + c)Example explained
Line 1split returns a list of five strings, and the star keeps the reading count open-ended.
Line 2temps holds strings, so the conversion to float is a separate step; unpacking never converts types.
Line 3`_` is an ordinary name used twice as a convention for values you do not need; the second binding overwrites the first.
Line 4The nested pattern mirrors the shape of the data, so a and b come from the inner tuple.
Unpacking consumes an iterator
Shows that unpacking pulls items out of a generator, leaving it partly or fully exhausted.
gen = (n * n for n in range(5))
first, second, *tail = gen
print(first, second, tail)
print(list(gen))
counter = iter([1, 2, 3, 4])
a, b = next(counter), next(counter)
print(a, b, list(counter))Example explained
Line 1The starred target forces the generator to run to completion so it can collect the leftovers.
Line 2list(gen) is empty afterwards because a generator cannot be restarted.
Line 3In the second block only two items are pulled explicitly, so the iterator still yields 3 and 4.
Line 4This is why unpacking an endless generator into a starred target never finishes.
Important notes
Unpacking works on any iterable, including sets and dicts, but sets have no reliable order and iterating a dict gives keys only, so `a, b = {'x': 1, 'y': 2}` binds 'x' and 'y'.
The star in `f(*args)` is argument unpacking in a call, a different feature from a starred assignment target, even though both use `*`.
Common mistakes
Expecting the starred name to be a tuple when the source is a tuple; it is always a list, so `first, *rest = (1, 2, 3)` gives rest == [2, 3] and `rest + (4,)` raises TypeError.
Using two stars, as in `a, *b, *c = data`; this is a SyntaxError at compile time, not a runtime error, so the whole file fails to load.
Writing `*a = "hi"` without a trailing comma; a starred name must be part of a target list, so you need `*a, = "hi"` to get ['h', 'i'].
Try it yourself
Change, predict, then run
Given `row = "berlin,10,12,15,9"`, unpack it into a city name and a list of integer temperatures using one starred target, then print the city with the highest and lowest of those temperatures.
Open the Python workspaceCheck your understanding
After `a, b = 1, 2` you run `a, b = b, a + b`. What does `print(a, b)` show?
- 2 3
- 2 4
- 1 3
- 3 5
Show answer
The tuple (b, a + b) is built entirely from the old values first, giving (2, 3), and only then are a and b rebound, so a is 2 and b is 3. '2 4' is what you would get if a were assigned before a + b was computed, which is not how multi-target assignment works: the right side is fully evaluated before any target is touched.