Multiple Assignment and Unpacking in Python: Tuples, Stars, and Swaps
- a, b = 1, 2 assigns both variables in one statement; the right side is really a tuple.
- Unpacking works with any iterable of the right length, not just tuples — lists, strings, and more.
- A single starred name, like *rest, collects any number of leftover items into a list.
- a, b = b, a swaps two variables without a temporary variable, because the right side is built before assignment happens.
Multiple assignment basics
Python lets you assign several variables in a single statement by separating both the names and the values with commas:
x, y = 1, 2
print(x, y) # 1 2
The right-hand side 1, 2 is actually a tuple, formed implicitly by the commas. The left-hand side unpacks that tuple, matching each variable to the value in the same position. The number of names on the left must match the number of values on the right, or Python raises ValueError.
Unpacking any iterable
Unpacking is not limited to literal tuples — it works with any iterable that produces the right number of items, including lists, strings, and the results of function calls:
first, second, third = [10, 20, 30]
print(first, third) # 10 30
a, b, c = "xyz"
print(a, b, c) # x y z
def min_max(values):
return min(values), max(values)
lo, hi = min_max([4, 9, 1, 7])
print(lo, hi) # 1 9
This is why iterating over a dict's .items() with for key, value in d.items() works: each iteration produces a two-item tuple that unpacks directly into key and value.
Star unpacking for extra items
A single name prefixed with * collects any number of remaining items into a list, letting you unpack sequences without knowing the exact length in advance:
first, *middle, last = [1, 2, 3, 4, 5]
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5
head, *rest = [1, 2, 3]
print(head, rest) # 1 [2, 3]
Only one starred name is allowed per unpacking statement, since Python needs to know how many fixed positions surround it to figure out how many items the star should absorb. If there are no remaining items, the starred variable becomes an empty list rather than raising an error.
The variable-swap idiom
Because the right-hand side of an assignment is fully evaluated into a tuple before any assignment happens, swapping two variables needs no temporary variable:
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1
Python builds the tuple (b, a) — evaluating the original values of b and a first — and only then assigns its two items back to a and b. This same principle extends to rotating any number of variables: a, b, c = c, a, b shifts all three in one step.
Nested unpacking
Unpacking can nest to match structures that are themselves made of smaller tuples or lists, which is useful when iterating over grouped data:
points = [(1, (2, 3)), (4, (5, 6))]
for label, (x, y) in points:
print(label, x, y)
# 1 2 3
# 4 5 6
Here each outer item is a two-element tuple, and its second element is itself unpacked into x and y in the same for statement — no separate line is needed to pull the inner pair apart.
Ignoring values with underscore
By convention, a single underscore _ is used as a variable name to mean "I need this position filled, but I do not care about its value":
_, month, _ = (2026, 7, 6)
print(month) # 7
first, *_ = [1, 2, 3, 4, 5]
print(first) # 1
_ has no special meaning to the interpreter itself — it is a perfectly ordinary variable name — but the convention signals intent clearly to anyone reading the code, and avoids inventing throwaway names like unused1 and unused2 for values that will never be read again. Because it is an ordinary name, reusing _ more than once in the same unpacking statement is not allowed if the star form is combined with it more than once, since Python still enforces that only a single starred name may appear per statement.
Star-unpacking into function calls
The * operator also works in reverse, at a function call site: it spreads an existing list or tuple out into separate positional arguments, instead of collecting arguments into one:
def add(a, b, c):
return a + b + c
values = [1, 2, 3]
print(add(*values)) # 6, equivalent to add(1, 2, 3)
def describe(name, age):
return f"{name} is {age}"
data = {"name": "Ana", "age": 28}
print(describe(**data)) # Ana is 28
A single * unpacks a sequence into positional arguments in order; a double ** unpacks a dict into keyword arguments, matching each key to a parameter name. This is the calling-side counterpart to defining a function with *args and **kwargs, and is especially useful for forwarding a collected set of arguments on to another function unchanged.