Why this matters
Often a few values belong together: a point's x and y, a record's name and age, the minimum and maximum of some data. A tuple groups them into a single immutable value. Tuples come with unpacking, one of Python's most pleasant features, which lets you pull the values back out into names in one line. You already used tuples without noticing: *args collects arguments into one, and a, b = b, a swaps two variables by packing and unpacking a tuple.
Tuples
A tuple is an ordered, immutable sequence. You write it as values separated by commas, usually in parentheses:
point = (3, 4)
person = ("Ada", 1815, "London")
print(point[0], person[-1], len(person))
print(person[1:])
3 London 3
(1815, 'London')
Indexing, slicing, len, in, +, *, iteration, count and index work just as for lists. What you cannot do is change a tuple:
point[0] = 10
TypeError: 'tuple' object does not support item assignment
There is no append, remove or sort either. To "change" a tuple you build a new one: point = (10,) + point[1:].
The comma makes the tuple
The parentheses are mostly for grouping and readability; the comma is what creates a tuple:
a = 1, 2, 3 # a tuple
b = (5) # just the int 5
c = (5,) # a one-element tuple
d = () # the empty tuple (the one case without a comma)
print(type(a), type(b), type(c), type(d))
<class 'tuple'> <class 'int'> <class 'tuple'> <class 'tuple'>
Why have an immutable list?
- Meaning. A list is usually a collection of similar things whose length varies (all the scores). A tuple is usually a fixed-size record whose positions mean different things (name, year, city). The choice tells readers what to expect.
- Safety. A tuple you pass to a function cannot be modified by it.
- Hashability. Because they cannot change, tuples (of immutable items) can be used as dictionary keys and set members, which lists cannot. You will use this in the next two lessons:
distances[("Paris", "Rome")] = 1420. - Efficiency. A tuple is a fixed-size array of references allocated in one block, with no spare capacity; it is slightly smaller and faster to create than a list. The compiler can even store a tuple of constants such as
(1, 2, 3)as a single ready-made constant in the bytecode.
Immutability is shallow: a tuple holds references, and if one of them points to a mutable object, that object can still change:
t = ("tags", ["a", "b"])
t[1].append("c")
print(t)
('tags', ['a', 'b', 'c'])
The tuple still refers to the same two objects; it is the list that changed. (Such a tuple is not hashable.)
Packing and unpacking
Writing values separated by commas packs them into a tuple. Putting several names on the left of = unpacks a sequence into them:
record = ("Grace", "Hopper", 1906) # packing
first, last, born = record # unpacking
print(last, born)
Hopper 1906
Unpacking works with any iterable (lists, strings, the result of split), as long as the counts match:
host, port = "example.com:443".split(":")
x, y, z = [1, 2, 3]
a, b = "hi"
print(host, port, x + y + z, a, b)
example.com 443 6 h i
A mismatch raises ValueError: too many values to unpack (expected 2) or ValueError: not enough values to unpack (expected 3, got 2).
Swapping, and how it works
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1
The right-hand side b, a is evaluated first, completely, producing a tuple (2, 1) (CPython actually optimizes small cases to a stack swap without building the tuple). Only then are the names on the left bound. That is why no temporary variable is needed, and why the Fibonacci update a, b = b, a + b uses the old a and b on the right.
Star unpacking
A name prefixed with * in an unpacking target collects "the rest" as a list:
first, *rest = [1, 2, 3, 4]
*init, last = "abcde"
head, *middle, tail = (10, 20, 30, 40)
print(first, rest, init, last, head, middle, tail)
1 [2, 3, 4] ['a', 'b', 'c', 'd'] e 10 [20, 30] 40
Only one starred name is allowed per unpacking, and it may receive an empty list.
Unpacking in for loops
Loops unpack each item too, which is very handy for sequences of pairs:
pairs = [("Ada", 36), ("Alan", 41), ("Grace", 85)]
for name, age in pairs:
print(name, "is", age)
The iteration module builds on this with enumerate and zip, which produce exactly such pairs.
Returning multiple values
A function returns one object, but that object can be a tuple, and unpacking at the call site makes it feel like several return values:
def divide(a, b):
"""Return (quotient, remainder) of integer division."""
return a // b, a % b
q, r = divide(17, 5)
print(q, r)
print(divide(17, 5))
3 2
(3, 2)
The built-in divmod(17, 5) does exactly this.
Named tuples, briefly
When a tuple has more than two or three fields, person[2] is cryptic. The standard library's collections.namedtuple creates a tuple type whose fields also have names:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p[1], p)
x, y = p # still a tuple: unpacking works
3 4 Point(x=3, y=4)
Named tuples are still immutable tuples, just more readable. In the object-oriented module you will meet dataclasses, the more flexible modern alternative for records.
Common mistakes
- Forgetting the comma in a one-element tuple:
("solo")is a string. Write("solo",). - Trailing comma by accident:
total = 5,makestotalthe tuple(5,), and later arithmetic fails withTypeError: can only concatenate tuple (not "int") to tuple. - Count mismatch when unpacking
splitresults from messy input:ValueError: not enough values to unpack. Check the data, or usestr.partition. - Trying to modify a tuple: build a new one, or use a list if the collection really should change.
Recap
- Tuples are immutable sequences, made by commas:
1, 2or(1, 2);(x,)has one element. - Use tuples for fixed-shape records, lists for variable collections of similar items.
- Unpacking binds names from any iterable:
a, b = pair,first, *rest = items, andfor k, v in pairs:. a, b = b, aworks because the right side is fully evaluated before binding.- Functions return multiple values by returning a tuple;
namedtupleadds field names.
Going further: read "Tuples and Sequences" in the official tutorial (section 5.3). In the REPL, try import dis; dis.dis("a, b = b, a") and look for the SWAP instruction.