PythonMastery
reference 5 min read · lesson 6 of 16 in Reference

Python Tuple Cheat Sheet

1 · The lesson

read

Immutable ordered sequence. Like a list, but you cannot change it after creation. Hashable (if its items are), so usable as dict keys and set members. For the full tutorial, see Tuples.


Creating

python
a = (1, 2, 3)
b = 1, 2, 3                 # parens optional — still a tuple
c = ()                      # empty
d = (42,)                   # SINGLE element — comma required
e = (42)                    # NOT a tuple — just int 42
f = tuple([1, 2, 3])        # from iterable
g = tuple("abc")            # ('a', 'b', 'c')
h = tuple(range(3))         # (0, 1, 2)

The comma — not the parens — makes the tuple. (42,) is a one-tuple; (42) is an int.

Indexing & Slicing

Same syntax as lists, but read-only:

python
t = (10, 20, 30, 40, 50)
t[0]        # 10
t[-1]       # 50
t[1:4]      # (20, 30, 40)
t[::-1]     # (50, 40, 30, 20, 10)
len(t)      # 5
30 in t     # True
t.count(20) # 1
t.index(30) # 2

t[0] = 99   # TypeError — immutable

Only two methods: .count() and .index().

Why Use Tuples

ReasonDetail
HashableCan be a dict key or set element (if items hashable too)
Immutable contractSignals "don't change this" — safer to pass around
Function returnsMultiple return values are tuples: return a, b
FasterSlightly faster than lists, smaller memory footprint
Fixed schemaCoordinates, RGB, (x, y, z) — known shape
python
# dict key
locations = {(40.7, -74.0): "NYC", (51.5, -0.1): "London"}

# set element
seen = {(1, 2), (3, 4)}

# Lists can't do either — unhashable
{[1, 2]}   # TypeError

Unpacking

python
t = (1, 2, 3)
a, b, c = t                 # 1, 2, 3
a, b = 1, 2                 # without parens — same thing

# Starred (3.0+)
first, *rest = (1, 2, 3, 4)         # first=1, rest=[2, 3, 4]
*init, last = (1, 2, 3, 4)          # init=[1, 2, 3], last=4
a, *mid, z = (1, 2, 3, 4, 5)        # a=1, mid=[2,3,4], z=5

# Ignore with _
name, _, age = ("Ada", "ignored", 36)
for _, value in items:              # ignore key
    ...

# Nested
((a, b), c) = ((1, 2), 3)           # a=1, b=2, c=3
+ setup added so this can run · defines items
items = [("alpha", 1), ("beta", 2), ("gamma", 3)]

namedtuple (collections)

python
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
p.x                 # 1
p[0]                # 1 — still indexable
p._asdict()         # {'x': 1, 'y': 2}
p._replace(x=99)    # Point(x=99, y=2) — new instance

typing.NamedTuple

Modern, typed, class-syntax version:

python
from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int = 0          # default value

    def magnitude(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5

p = Point(3, 4)
p.magnitude()           # 5.0

See Type Hints and Collections Cheat Sheet.

Common Patterns

python
# Swap variables
a, b = b, a

# Multiple return values
def divmod_(a, b):
    return a // b, a % b

q, r = divmod_(17, 5)

# Coordinate pairs
point = (3, 4)
x, y = point

# As dict keys for compound lookups
cache = {}
cache[(user_id, date)] = result

# Tuple of args (splat in)
args = (1, 2, 3)
sum_three(*args)            # sum_three(1, 2, 3)

# Convert list -> tuple (e.g. to use as dict key)
key = tuple(sorted(items))

# Enumerate yields tuples
for i, x in enumerate(xs):
    ...

# zip yields tuples
for a, b in zip(xs, ys):
    ...
+ setup added so this can run · defines result, sum_three, xs, ys, user_id, date, items
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

result = _AutoMock('result')
def sum_three(*_a, **_kw):
    print('-> sum_three() called')
    return _AutoMock('sum_three()')
xs = _AutoMock('xs')
ys = _AutoMock('ys')
user_id = _AutoMock('user_id')
date = _AutoMock('date')
items = _AutoMock('items')

Tuple vs List

PropertyTupleList
MutableNoYes
HashableYes (if items are)No
Syntax(1, 2)[1, 2]
Methods2 (count, index)~11
Use caseFixed records, keysGrowing collections
PerformanceSlightly fasterSlightly slower

Common Errors

ErrorCauseFix
TypeError: 'tuple' object does not support item assignmentt[0] = xTuples are immutable. Build a new tuple: (x,) + t[1:], or use a list
TypeError: 'tuple' object has no attribute 'append'Treating tuple as listUse a list, or t = t + (x,) to make a new tuple
ValueError: too many values to unpack (expected 2)a, b = (1, 2, 3)Match counts: a, b, c = ... or use a, b, *_ = ...
ValueError: not enough values to unpack (expected 3, got 2)a, b, c = (1, 2)Right-hand side must have enough items
(42) not a tupleMissing trailing comma(42,) for a one-tuple
TypeError: unhashable type: 'list' when using tuple as keyTuple contains a listAll items must be hashable; convert inner list to tuple

See Also