Python List Unpacking with the Star Operator
python list unpacking with star: Learn how Python list unpacking with the star operator collects variable-length tails, merges sequences, and expands iterables in func...
Python list unpacking with star (*) lets you assign the leading, middle, or trailing elements of a sequence to named variables while collecting the rest into a list. The syntax was introduced in Python 3.0 through PEP 3132 and has become the standard way to handle variable-length sequences without slicing or manual indexing.
first, *rest = [10, 20, 30, 40] print(first) # 10 print(rest) # [20, 30, 40]
The starred target rest receives a list containing every element not bound to the other targets. This works whether the source is a list, tuple, string, or any other iterable.
The Basic Assignment Behavior
In an assignment, the star operator marks one target as the collector for all remaining items. The collector always receives a list, even when the source is a tuple or a string.
head, *tail = (1, 2, 3) print(head) # 1 print(tail) # [2, 3]
Only one starred target may appear in a single assignment. Python raises a SyntaxError if you try to use two:
a, *b, *c = [1, 2, 3] # SyntaxError: multiple starred expressions in assignment
The starred target can appear in any position, which makes it possible to capture the middle of a sequence:
first, *middle, last = [1, 2, 3, 4, 5] print(first) # 1 print(middle) # [2, 3, 4] print(last) # 5
When the sequence is exactly long enough to fill the fixed targets, the starred target receives an empty list:
a, *b = [1] print(a) # 1 print(b) # []
Unpacking Arguments in Function Calls
The same star operator, used inside a function call, expands an iterable into positional arguments. This is the inverse of collecting in an assignment.
def add(a, b, c): return a + b + c values = [1, 2, 3] result = add(*values) # add(1, 2, 3)
This becomes especially useful when a function accepts a variable number of positional arguments:
def log(level, *messages): for msg in messages: print(f"[{level}] {msg}") entries = ["connection lost", "retrying in 2s"] log("ERROR", *entries)
The star operator in a call works with any iterable, so you can pass a tuple, a generator, or a range object. It does not convert the values; it feeds them as separate positional arguments.
Merging Sequences in List, Tuple, and Set Displays
Python 3.5 (PEP 448) extended the star operator so it can appear inside list, tuple, and set literals. This makes concatenation more readable than chaining + operators.
left = [1, 2] right = [3, 4] combined = [*left, *right] print(combined) # [1, 2, 3, 4]
The same pattern works in tuple and set displays:
a = (1, 2) b = (3, 4) combined_tuple = (*a, *b) print(combined_tuple) # (1, 2, 3, 4) s1 = {1, 2} s2 = {2, 3} combined_set = {*s1, *s2} print(combined_set) # {1, 2, 3}
For sets, the star expansion naturally applies set semantics: duplicates are dropped. This is a concise way to union two sets without calling .union().
Note that [*a, *b] creates a new list. It does not mutate either source. This matters when the source lists are large or shared elsewhere in the program.
Common Mistakes and Edge Cases
The most common mistake is trying to use two starred targets in one assignment. Python rejects this at parse time:
a, *b, *c = [1, 2, 3, 4] # SyntaxError: multiple starred expressions in assignment
Another frequent error is using the star operator with a non-iterable value. The star operator requires an iterable; passing an integer raises TypeError:
a, *b = 42 # TypeError: cannot unpack non-iterable int object
A subtle behavior worth remembering: the starred target always produces a list, even when the source is a string or a generator. If you need the collected items in the original type, you must convert explicitly:
first, *rest = "hello" print(rest) # ['e', 'l', 'l', 'o']
When unpacking a generator, the starred target consumes the generator entirely. If the generator is infinite, the assignment never completes:
def infinite(): n = 0 while True: yield n n += 1 first, *rest = infinite() # never terminates
Runtime Behavior and Memory Considerations
The starred target collects all remaining elements into a new list. For a sequence of length n, the collector holds n - k elements, where k is the number of fixed targets. The memory cost is proportional to the number of collected items.
This is rarely a problem for typical list sizes, but it matters when unpacking a large generator or an unbounded stream. The star operator materializes the entire remainder; it is not a lazy operation.
If you only need the first element and want to avoid materializing the rest, use next() on an iterator instead:
iterator = iter(large_iterable) first = next(iterator)
This avoids building a list of the remaining items. The star approach is cleaner when you genuinely need the remainder.
For merging lists, [*a, *b] creates a new list and copies references from both sources. The time cost is O(len(a) + len(b)), which is equivalent to a + b for lists. The star syntax has the advantage of working uniformly across list, tuple, and set displays.
When to Prefer Star Unpacking over Slicing
Slicing can achieve similar results but is less readable for variable-length tails:
values = [1, 2, 3, 4, 5] first, rest = values[0], values[1:]
The slice values[1:] returns a list, so the type matches. But the intent is less obvious, and the approach requires manual handling of the empty-sequence case. Star unpacking raises a clear ValueError when the fixed targets cannot be satisfied:
first, *rest = [] # ValueError: not enough values to unpack
Star unpacking also handles the boundary case where the sequence has exactly as many elements as fixed targets, producing an empty list for the starred target.
Choosing the Right Approach
Use star unpacking when you need to separate the first element from the rest of a sequence, capture the middle of a sequence between fixed endpoints, merge multiple iterables into a new list, tuple, or set, or pass an iterable's elements as separate function arguments.
Use explicit indexing or next() when you only need one element and want to avoid materializing the rest, when the source is an unbounded generator, or when you need to preserve the source type exactly without conversion.
The star operator is not a replacement for every unpacking pattern. It is a precise tool for variable-length collection and argument expansion, and it keeps the intent visible in the code rather than hiding it behind index arithmetic.