Python List vs Tuple: Unpacking and Named Tuples Explained
A Python tuple is an immutable sequence for fixed records, multiple return values and dict keys. Tuple vs list, one-element tuples, unpacking and namedtuple.
- Course: Python study plan
- Module: Lists, tuples and sequences
- Kind: Lesson
- Reading time: 13 min
- Runtime: CPython 3.11
What is the difference between a list and a tuple in Python?
A list is mutable and a tuple is immutable. A list holds a variable number of items of the same kind and grows with append; a tuple holds a fixed-shape record such as a coordinate, carries multiple return values and, when every element is hashable, can be a dictionary key or set member. Tuples are also slightly smaller and faster to build.
Lesson
A tuple is an immutable sequence, and that one property makes it the right type for three jobs: a fixed-shape record ((name, age, city)), a value returned from a function that has several results, and a key in a dictionary or a member of a set. Python's unpacking syntax — a, b = pair, first, *rest = xs, the parallel assignment a, b = b, a — is what makes tuples pleasant rather than clumsy. This lesson fixes the literal syntax (the comma, not the parentheses), the immutability rule and its one exception, unpacking in all its forms, and namedtuple for records whose fields deserve names.
Literals — it is the comma
point = (3, 4)
point = 3, 4 # the same tuple: the comma makes it, the parentheses group
single = (5,) # a one-element tuple needs the trailing comma
not_a_tuple = (5) # just the int 5 in parentheses
empty = ()
from_list = tuple([1, 2, 3])
Parentheses are required only where the comma would be ambiguous — inside a function call, a subscript or a larger expression — and by convention for readability. type((1)) is int; type((1,)) is tuple. Everything a string or list can do read-only, a tuple can do: index, slice, len, in, iterate, count, index, compare lexicographically, concatenate with +, repeat with *.
Immutable, with a caveat
t = (1, 2, 3)
t[0] = 9 # TypeError: 'tuple' object does not support item assignment
t = t + (4,) # a new tuple; the name rebinds
t = ([1, 2], 3)
t[0].append(9) # fine — the tuple still holds the same list object, which changed
t[0] = [0] # TypeError — the *slot* cannot be rebound
The tuple's structure is fixed: which objects it references, in what order. Whether those objects change is up to them. A tuple of numbers and strings is therefore fully immutable and hashable; a tuple containing a list is neither hashable ({([1], 2)} raises TypeError: unhashable type: 'list') nor truly frozen.
Why tuples
- Records. A row from a file, a coordinate, an RGB triple: fixed length, position has meaning. Lists are for collections of the same kind of thing with a variable length.
- Multiple return values.
return lo, hibuilds a tuple; the caller unpacks it. - Dictionary keys and set members.
seen = {(r, c)},distances[(a, b)]— a list cannot be a key (Module 7). - Safety. A function receiving a tuple cannot accidentally append to it.
- Speed and size. Slightly smaller and faster to build than a list; a constant tuple is built once at compile time.
Unpacking
a, b = 1, 2 # parallel assignment
a, b = b, a # swap: the right side is built before any name is bound
x, y, z = "xyz" # any iterable of the right length
(name, age), city = ("Ada", 36), "London" # nested
first, *middle, last = [1, 2, 3, 4, 5] # first=1, middle=[2, 3, 4], last=5
head, *tail = [1] # head=1, tail=[]
*init, last = xs
The counts must match, or ValueError: too many values to unpack (expected 2) / not enough values. A starred target absorbs the surplus as a list (possibly empty) and may appear once, anywhere. Unpacking works in every binding position — for k, v in d.items(), for i, (a, b) in enumerate(pairs), lambda p: p[0] versus def f(p): x, y = p, and in function calls as f(*args).
The swap works because the right-hand tuple is fully evaluated first; xs[i], xs[j] = xs[j], xs[i] swaps list elements in one line for the same reason.
Ignoring values
_ is the conventional name for a value you do not need: for _ in range(3), name, _, city = row, *_, last = xs. It is an ordinary name and nothing stops you reading it; the convention is for the reader.
Named tuples
A tuple whose fields have names, with the tuple's size and immutability and a readable repr:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x, p[0] # 3, 3 — by name or by position
x, y = p # still unpacks
p._replace(x=0) # a new Point(0, 4)
p._asdict() # {'x': 3, 'y': 4}
Point._fields # ('x', 'y')
print(p) # Point(x=3, y=4)
typing.NamedTuple is the class-syntax version with type hints:
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int = 0
def norm(self) -> float:
return (self.x ** 2 + self.y ** 2) ** 0.5
Named tuples are the lightest way to give a record a shape; when you need mutability, defaults with factories or methods that change state, Module 8's dataclass is the next step up.
Pitfalls
(5)when(5,)was meant.- Assuming a tuple with a list inside is hashable or frozen.
- Unpacking a row with the wrong number of fields — the
ValueErroris the input validation. - Using a list for a fixed record (
row[2]with no meaning) where a named tuple would document the fields. - Writing
return (a, b)andx = f()[0]when unpacking would be clearer. - Two starred targets in one unpacking (a syntax error).
Key takeaways
- The comma makes a tuple;
(5,)is a one-tuple,(5)is an int. - Tuples fix their structure, not their contents; a tuple of immutables is hashable and a valid dict key.
- Use tuples for fixed-shape records, multiple returns and keys; lists for variable collections of like items.
a, b = b, a,first, *rest = xs, nested targets, and unpacking inforand calls; counts must match.namedtuple/NamedTupleadd field names,_replaceand_asdictto a tuple;dataclassis the mutable step up.
Common questions
How do I create a tuple with one element in Python?
Add a trailing comma: (5,) or just 5,. The comma makes a tuple and the parentheses only group, so (5) is the integer 5 — type((1)) is int while type((1,)) is tuple. An empty tuple is ().
Are Python tuples really immutable?
A tuple's structure is fixed — which objects it holds, in what order — but those objects may still change. With t = ([1, 2], 3), t[0].append(9) succeeds while t[0] = [0] raises TypeError. A tuple that contains a list is not hashable either.
What is tuple unpacking in Python?
Unpacking assigns the elements of an iterable to several names at once: a, b = pair, first, *rest = xs, or the swap a, b = b, a. The counts must match or Python raises ValueError: too many values to unpack; one starred target collects the surplus as a list, possibly empty.
How does a, b = b, a swap two variables?
The right-hand side b, a is evaluated first into a tuple, and only then unpacked into a and b, so neither value is lost. The same rule lets xs[i], xs[j] = xs[j], xs[i] swap two list elements in one line.
What is a namedtuple in Python?
collections.namedtuple creates a tuple subclass whose fields have names: Point = namedtuple("Point", ["x", "y"]) gives p.x as well as p[0], a readable repr, _replace and _asdict. typing.NamedTuple is the class-syntax version with type hints; a dataclass is the step up when a record must be mutable.
Exercises
Records with a star
Each line holds a name, then any number of integer scores (possibly none), then a city. Unpack every line with one starred assignment — name, *scores, city = tokens — and print the name, the city and the mean score with two decimals, or no scores.
Input: lines. Output: <name> <city> <mean> or <name> <city> no scores per line.
ada 90 80 london
bob paris
prints
ada london 85.00
bob paris no scoresNamed points
Define Point = namedtuple("Point", "x y"). Read n points and print each one's repr with its distance from the origin (two decimals); then the farthest point (max with a key; first on ties); then that point reflected through the origin using _replace.
Input: n, then n lines x y. Output: n lines Point(x=..., y=...) <dist>, then farthest: <repr>, then reflected: <repr>.
2
3 4
-1 0
prints
Point(x=3, y=4) 5.00
Point(x=-1, y=0) 1.00
farthest: Point(x=3, y=4)
reflected: Point(x=-3, y=-4)In this module: Lists, tuples and sequences
- Lists — the mutable sequence
- Comprehensions — building collections from expressions
- Sorting — sort, sorted, keys, stability and bisect
- Tuples, unpacking and named tuples (this lesson)
- Grids and nested lists
- The sequence tools — enumerate, zip, reversed, any, all, deque and the protocol
- Checkpoint — Lists, tuples and sequences
← Sorting — sort, sorted, keys, stability and bisect · Grids and nested lists →