Python Unpacking
Beginner Data Structures

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.

GoalBy the end you can unpack lists, strings and nested data, collect extras with a starred name, swap values, unpack in for loops, and use * and ** in calls and merges.

Before this lesson: Python Lists, Python Tuples

2Python Unpacking: diagram

Python Unpackingscores = [98, 91, 85, 77, 60]names on the leftwhat each one receivesfirst98*middle[91, 85, 77]last60first, *middle, last = scores (the starred name collects the leftovers as a list)
On the left of the equals sign the plain names take one item each and the starred name collects everything between them as a list.

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.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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.

Python
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

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.

Interactive example
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.

  1. Line 1A line of comma-separated text: a name, a city and three marks.
  2. Line 2split(",") returns a list of five strings. name and city take the first two, and *marks collects the remaining three as a list: ['82', '91', '77'].
  3. Line 3The marks are still text, so a list comprehension turns each one into an int.
  4. Line 4sorted(..., reverse=True) gives [91, 82, 77]. The first value goes to best and *rest collects [82, 77].
  5. Line 5-6Printing shows the pieces: Asha from Pune and the best mark with the others.
  6. Line 8A small dictionary built from the variables we just unpacked.
  7. Line 9-10{**defaults, **profile} pours both dictionaries into a new one. profile comes last, so its "city" replaces the default and "name" is added. The original dictionaries are not changed.
  8. Line 11The merged dictionary keeps the key order of defaults first, then any new keys: city, active, name.
  9. Line 13-14A function with two parameters named name and city.
  10. Line 15greet(**profile) turns each key into a keyword argument, so it is the same as greet(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
ValueError: too many values to unpack (expected 2)

✓ 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)
ValueError: too many values to unpack (expected 2)

✓ 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]
SyntaxError: multiple starred expressions in assignment

✓ 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))
TypeError: greet() got an unexpected keyword argument 'town'

✓ 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)
No error, but it prints 2 2: the first assignment destroyed the old value of a

✓ 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

Write three statements, each using unpacking:

  1. Unpack the list record into the names name, city and age.
  2. Unpack date.split("-") into year, month and day.
  3. low and high were typed the wrong way round. Swap them with one statement, so that low is 3 and high is 9.

Finally print name and year.

Your code
record = ["Asha", "Pune", 28]
date = "2025-03-14"
low, high = 9, 3

# your code here

2. Predict the unpacking

Predict the output Medium

Read the code and type exactly what it will print, line by line. Remember that a starred name always collects its items into a list.

Read this code
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)

2 more exercises → 6 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

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