Command Palette

Search for a command to run...

Hectal
PHASE 4Beginner ~27 min· topic 3 of 11

Topic 4.3

Tuples and Unpacking

In one line

A tuple is an ordered collection that cannot be changed after it is created, written with round brackets. Tuples are the natural way to bundle a few related values, and unpacking lets you split a tuple (or any sequence) into separate variables in one line.

Think of it like this

A map coordinate such as latitude and longitude, or a name written on a printed ID card. Once it is printed, you don't edit it; if the data changes you print a new card. A tuple is a list that is locked: it keeps the order, it allows duplicates, you can read items by index, but you cannot add, remove or replace anything.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Tuple
An ordered collection that cannot be changed after it is created. Written with round brackets.
Immutable
Cannot be changed after creation. Numbers, strings and tuples are immutable.
Packing
Putting several values into one tuple: row = 1, 2, 3.
Unpacking
Splitting a sequence into separate variables: a, b, c = row.
Star unpacking
Using *name to collect the leftover items into a list.
Record
A bundle of related values, each with its own meaning, such as (name, age, city).
Hashable
Has a fixed value that Python can turn into a number for fast lookup, so it can be a dictionary key. Immutable tuples of hashable items are hashable.
Named tuple
A tuple whose positions also have names, made with collections.namedtuple.

Step by step

01Make a tuple and read it

A tuple reads like a list: indexes, negative indexes, slices, len, in, count and index all work. What is missing is anything that changes it, so there is no append, remove or sort on a tuple.

main.pywhole filepython
point = (3, 4)
print(point[0])        # 3
print(point[-1])       # 4
print(len(point))      # 2
print(point + (5,))    # (3, 4, 5)  a NEW tuple
print(type((5)), type((5,)))

02The comma makes the tuple

Brackets are only needed to avoid confusion. x = 5, creates a one-item tuple. x = (5) is the number 5 with pointless brackets. The same rule explains print(1, 2): that is two separate values handed to print, not a tuple, because the brackets there belong to the call.

03Immutable means the slots are fixed

Assigning to a slot fails. Appending to a list inside a tuple works, because that does not change which object the slot points at. Immutability protects the tuple's structure, not the objects it refers to.

main.pywhole filepython
t = (1, [2, 3])
t[1].append(4)
print(t)        # (1, [2, 3, 4])
# t[0] = 9      # TypeError: 'tuple' object does not support item assignment
Immutable means the slots are fixeddiagram
Rendering diagram…

04Packing and unpacking

Unpacking assigns by position. The left side needs the same number of names as there are items. Underscore _ is a common name for a value you don't need.

main.pywhole filepython
row = "Asha", 21, "Delhi"      # packing
name, age, city = row           # unpacking
print(name, age, city)

name, _, city = row             # ignore the age
print(name, city)

05Swap with a tuple

In other languages swapping needs a third variable. In Python the right side b, a is evaluated completely first, into a tuple, before any name on the left is changed.

main.pywhole filepython
a = 1
b = 2
a, b = b, a
print(a, b)     # 2 1

06Star unpacking

*rest collects the items that the plain names did not take, and always gives a list (even when the source was a tuple). It can sit at the start, middle or end. It is empty ([]) when there is nothing left.

main.pywhole filepython
first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last)     # 1 [2, 3, 4] 5

head, *tail = "abc"
print(head, tail)              # a ['b', 'c']

07Tuples as records and keys

A tuple of numbers or strings can be a dictionary key, which a list cannot. That is ideal for a grid position, (row, column), or a route, (from_city, to_city). For records with many fields, a named tuple adds names to the positions. The curly brackets below make a dictionary (Topic 4.4): a lookup table where a key finds a value. The first line, from collections import namedtuple, fetches a ready-made tool; Topic 6.1 teaches import, so copy it as it is for now.

main.pywhole filepython
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p[1])               # 3 4

distance = {("Delhi", "Mumbai"): 1400}
print(distance[("Delhi", "Mumbai")])

Try it yourself

  1. 1

    Predict the types

    Before running the first example, decide what type((5)) and type((5,)) are. Then remove the comma in single = (5,) and run again.

  2. 2

    Unpack a longer record

    In the first example, change row to "Asha", 21, "Delhi", "India". Run it. Then fix it by adding a fourth name, or by using *extra at the end of the left side.

Code & diagrams

Tuples, packing, unpacking and swapping New tab

Add a fourth value to row and run it: what error do you get when unpacking into three names? (`type(x).__name__` just gives the name of x's type as text.)

Sign in to run this example in your browser.

Expected output

point: (3, 4) | first: 3 | length: 2
single: tuple | not_a_tuple: int
Asha is 21 and lives in Delhi
swapped: 2 1
first: 1 | middle: [2, 3, 4] | last: 5
the list inside changed: (1, [2, 3, 4])
Tuples as records and dictionary keys New tab

The last line shows that tuples sort item by item: first items first, and the second items break ties. `from collections import namedtuple` fetches a ready-made tool (Topic 6.1 teaches import).

Sign in to run this example in your browser.

Expected output

Point(x=3, y=4)
x = 3 | y = 4 | p[1] = 4
unpacked: 3 4
Delhi to Mumbai: 1400 km
at (2, 3): treasure
[(1, 'z'), (2, 'a'), (2, 'b')]

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Change a tuple

Try to replace an item of a tuple.

main.pywhole filepython
point = (3, 4)
point[0] = 10
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 2, in <module>
point[0] = 10
~~~~~^^^
TypeError: 'tuple' object does not support item assignment

Break #2

Unpack with the wrong number of names

Give three values to two names.

main.pywhole filepython
a, b = (1, 2, 3)
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 1, in <module>
a, b = (1, 2, 3)
^^^^
ValueError: too many values to unpack (expected 2)

Myth vs fact

Myth

Tuples are just slow lists with fewer features.

Fact

They are lighter and slightly faster to create, but the real value is meaning: a tuple says "this group of values is one fixed thing". They can also be dictionary keys.

Myth

The round brackets make a tuple.

Fact

The commas make it. x = 1, 2 is a tuple. The one exception is the empty tuple (). A single item needs the comma: (1,).

Myth

Everything inside a tuple is frozen.

Fact

Only the tuple's own slots are fixed. A list inside a tuple can still be changed.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    CPython can store constant tuples in the compiled code and reuse them, and creating a small tuple is cheaper than creating a list, because a tuple never needs spare room to grow. The difference is small, so choose by meaning first. The swap a, b = b, a does not even build a tuple in CPython: the compiler emits a direct stack swap (dis.dis shows it, Topic 13.3), and the meaning is still the "evaluate the right side first" rule, so you can think in tuples.

  • ▸

    A tuple is hashable only if all its items are. hash((1, [2])) raises TypeError: unhashable type: 'list'. This is why (row, col) pairs of ints work as dictionary keys and ("a", [1]) does not.

  • ▸

    A famous puzzle: t = (1, [2]); t[1] += [3] raises TypeError and the list still becomes [2, 3]. The in-place addition works on the list, then Python tries to store the result back into the tuple slot and fails. Moral: don't use += on a list that lives inside a tuple.

  • ▸

    Records with many fields are better as a dataclass (Topic 8.8) or a Pydantic model (Topic 15.4) than as a long tuple: names catch mistakes that positions hide. Libraries often return tuples (such as a shape (rows, columns) in NumPy), and you unpack them in one line.

Remember this

  1. 1

    You write a tuple with round brackets and commas: point = (3, 4). The commas make the tuple, not the brackets: point = 3, 4 is the same. An empty tuple is (). The one-item tuple needs a trailing comma: (5,) is a tuple with one item, while (5) is just the number 5 in brackets. That small comma is a classic stumbling block.

  2. 2

    Tuples are immutable, which means they cannot be changed after they are made: point[0] = 10 raises TypeError. A tuple, like a list, has numbered slots (positions that each point at a value). "Locked" applies only to the tuple's own slots. If a slot holds a list, that list can still be changed: t = (1, [2, 3]) then t[1].append(4) works. The tuple still points at the same list; the list's contents moved.

  3. 3

    Packing means putting values into a tuple: row = "Asha", 21, "Delhi". Unpacking is the reverse: name, age, city = row gives each variable one item. The number of names on the left must match the number of items, or Python raises ValueError. Unpacking works with any sequence (an ordered collection such as a list, a string or a tuple). It is also how for index, item in enumerate(items) gets two values per loop (Topic 2.6), and how for key, value in d.items() does the same for dictionaries (Topic 4.4).

  4. 4

    Unpacking is also how you swap two variables without a helper: a, b = b, a. Python first builds the tuple (b, a) on the right and then unpacks it, so nothing is lost. A *name on the left collects "all the rest" into a list: first, *middle, last = [1, 2, 3, 4, 5] gives first = 1, middle = [2, 3, 4], last = 5. Only one starred name is allowed in one unpacking.

  5. 5

    Use a tuple for a record of different things that belong together (name, age, city) or a fixed pair like a coordinate. Use a list for a collection of the same kind of thing that may grow. Functions return "two values" by returning a tuple, which you will see in Phase 5. Because a tuple of numbers or strings is immutable, it can be a dictionary key or a set member; a list cannot. (Dictionaries are Topic 4.4 and sets are Topic 4.6. Topic 4.5 explains why a list cannot be a key.)

  6. 6

    A named tuple gives each position a name, so p.x reads better than p[0]: Point = namedtuple("Point", ["x", "y"]) from the collections module, a ready-made toolbox that comes with Python (Topic 4.10 covers it fully, and Topic 8.8 shows dataclass as a richer alternative). It is still a tuple underneath and can be unpacked like one.

Explain it without notes

01

What is the difference between a list and a tuple, and when would you choose each?

02

Why is (5) not a tuple, and how do you write one?

03

How does a, b = b, a swap two variables?

04

What does first, *rest = items do?

05

Is every tuple usable as a dictionary key? What happens with t = (1, [2, 3])?

Practice

01

Make a tuple birthday = (15, "August", 2010). Unpack it into day, month, year and print 15 August 2010 using those variables.

02

Given a = 10 and b = 20, swap them without a third variable and print a = 20, b = 10.

03

Given scores = [60, 70, 80, 90, 100], use star unpacking to get the lowest, the highest and a list of the middle scores. Print all three.

Trade-offs

  • ↔

    Immutability buys safety and dictionary-key power, and costs convenience. Nobody can change a tuple by accident, and it can be hashed. But to "change" it you build a new one.

  • ↔

    Positions are compact but cryptic. row[2] means nothing until you remember what column 2 was. Named tuples, dataclasses or dictionaries cost a little more and read much better as the record grows.

Done when you can

  • Done when you can write a tuple, including a one-item tuple, and say what the comma does.

  • Done when you can unpack a tuple into variables and explain the ValueError for a wrong count.

  • Done when you can swap two variables and use first, *rest = items.

  • Done when you can explain why a tuple of ints can be a dictionary key and a list cannot.

  • Done when you can explain why a list inside a tuple can still change.