Python Sequence Unpacking: Syntax and Common Pitfalls
python sequence unpacking: Learn Python sequence unpacking: tuple and list unpacking, extended unpacking with *, nested patterns, and how to avoid ValueError.
Python sequence unpacking assigns each element of a sequence to a separate variable in a single statement. The sequence can be a tuple, list, string, or any iterable that supports indexing. The left side of the assignment must have the same number of targets as the length of the sequence.
point = (3, 7) x, y = point print(x, y) # 3 7
This works for lists as well:
rgb = [255, 128, 0] red, green, blue = rgb
The assignment happens element by element. If the number of variables does not match the sequence length, Python raises a ValueError with a message like "too many values to unpack" or "not enough values to unpack". This strictness is a feature: it forces you to handle the full structure of the data you receive.
Extended Unpacking with the Star Operator
Python 3 extended the unpacking syntax with the * operator, introduced in PEP 3132. It lets you capture a variable-length portion of the sequence into a list.
first, *middle, last = [1, 2, 3, 4, 5] print(first) # 1 print(middle) # [2, 3, 4] print(last) # 5
The starred target collects zero or more elements. It can appear in any position, but only once per assignment. If the sequence has fewer elements than the fixed targets require, you still get a ValueError. For example, a, *b, c = (1, 2) works with b == [], but a, b, *c = (1,) fails because there is not enough for both a and b.
Extended unpacking is particularly useful when you need to separate the first element, the last element, or both from the rest of a sequence without slicing.
Nested Unpacking for Structured Data
When a sequence contains other sequences, you can unpack them in a single statement by nesting the target patterns.
data = (("Alice", 30), ("Bob", 25)) for name, age in data: print(f"{name} is {age} years old")
You can also unpack a tuple of tuples directly:
(first_name, last_name), birth_year = (("John", "Doe"), 1985)
Nested unpacking reduces the need for intermediate variables and makes the structure of the data explicit. It works with any combination of sequences, including lists inside tuples, as long as the nesting depth matches.
Practical Uses in Everyday Code
Sequence unpacking appears frequently in Python code beyond simple variable assignment.
Swapping two variables is a classic use:
a, b = b, a
This works because the right side is evaluated first, producing a tuple, which is then unpacked.
Another common pattern is returning multiple values from a function:
def min_max(numbers): return min(numbers), max(numbers) low, high = min_max([4, 1, 9, 2])
Unpacking also simplifies iterating over dictionary items:
for key, value in config.items(): print(key, value)
In each case, unpacking replaces verbose indexing and temporary variables, making the intent clearer.
Common Mistakes and the ValueError They Cause
The most frequent error with sequence unpacking is a mismatch between the number of targets and the sequence length. This often happens when you assume a fixed structure but receive data of a different shape.
x, y = ((1, 2), (3, 4)) # ValueError: too many values to unpack (expected 2)
Here the right side has two elements, but each element is itself a tuple. You need nested unpacking to capture both inner values.
Another mistake is using a starred target incorrectly. For example, you cannot use two stars in the same assignment:
a, *b, *c = [1, 2, 3] # SyntaxError
Python requires exactly one starred expression per assignment.
When you intentionally want to ignore a value, the convention is to use an underscore:
_, y = point
This does not prevent the unpacking from happening; it simply assigns the first element to a throwaway name. The underscore is a regular variable name, not special syntax, so it still occupies a target position.
Unpacking in Loops, Function Calls, and Comprehensions
Unpacking is not limited to assignment statements. It works in for loops, function calls, and comprehensions.
In a for loop, each iteration unpacks the current element:
for name, score in students: print(name, score)
In a function call, you can unpack a sequence into positional arguments with the * operator:
def add(a, b, c): return a + b + c args = [1, 2, 3] print(add(*args))
Note that this is different from sequence unpacking in assignment; it is argument expansion. The same * syntax is reused, which can be confusing.
List comprehensions can also use unpacking when the iterable yields tuples:
pairs = [(1, 2), (3, 4)] sums = [a + b for a, b in pairs]
These patterns rely on the same underlying mechanism, so the rules about length matching and nested patterns apply.
Compatibility and Version Notes
Extended unpacking with * is available in Python 3.0 and later. It is not available in Python 2. If you maintain code that must run on Python 2, you cannot use this syntax and must rely on slicing or explicit indexing instead.
Nested unpacking and basic unpacking have existed since early Python versions, but the error messages and some edge cases changed over time. For example, in Python 3, unpacking works for any iterable, not just sequences, but the implementation may consume the iterator. This matters when you unpack a generator:
gen = (x for x in range(3)) a, b, c = gen
This consumes the generator completely. If the generator produces fewer or more than three values, you get a ValueError. This behavior is intentional because unpacking requires knowing the full length in advance.
When working with large sequences, unpacking does not copy the elements; it only binds names to the existing objects. The memory overhead is minimal, but if you unpack a very large list into many variables, you still hold references to all of them. In practice, this is rarely a concern.