Python Tuples — Language Specification 1. Spec Reference Primary source: Python Language Reference, §3.2 — Immutable Sequences https://docs.python.org/3/reference/datamodel.html#immutable-sequence-types Built-in tuple type: https://docs.python.org/3/library/stdtypes.html#tuple Tuple display grammar: §6.2.3 https://docs.python.org/3/reference/expressions.html#parenthesized-forms collections.namedtuple: https://docs.python.org/3/library/collections.html#collections.namedtuple typing.NamedTuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple Python 3.12 standard: All rules here apply to CPython 3.12 unless otherwise noted. 2.1 Tuple Display parenth_form ::= "(" [starred_expression | (starred_expression ",")
[(starred_expression ",")* starred_expression [","]]] ")"
2.2 Key Syntax Rules # A single-element tuple REQUIRES a trailing comma:
single_tuple ::= "(" expression "," ")"
empty_tuple ::= "(" ")"
multi_tuple ::= "(" expression ("," expression)+ [","] ")"
# Without parentheses (implicit tuple in assignment, return, etc.):
tuple_expr ::= expression ("," expression)+ [","]
2.3 Tuple Unpacking (Assignment) target_list ::= target ("," target)* [","]
starred_target ::= "*" target
unpacking_assign ::= target_list "=" expression
3. Core Rules and Constraints 3.1 Tuple Characteristics Ordered: elements maintain insertion order. Immutable: once created, elements cannot be added, removed, or replaced. Heterogeneous: elements may be of any type. Hashable if and only if all elements are hashable. Zero-indexed; supports negative indexing. Fixed size; no append, extend, or remove methods. 3.2 Immutability Semantics The tuple itself cannot be mutated: no item assignment, no del. If a tuple contains a mutable object (e.g., a list), that mutable object can be changed. t = (1, [2, 3]); t[1].append(4) is valid — the list is mutated, not the tuple. The tuple's hash depends on the hash of its elements; a tuple containing an unhashable element is itself unhashable. 3.3 Single-Element Tuple Syntax (42,) is a tuple; (42) is just the integer 42. The trailing comma is mandatory for a single-element tuple. Empty tuple: () or tuple(). 3.4 Comma Creates a Tuple In Python, the comma operator creates a tuple, not parentheses. a, b = 1, 2 — right side is a tuple (1, 2). return x, y — returns tuple (x, y). a, = [1] — unpacks one-element iterable into a (note the comma). 3.5 Unpacking Rules The number of targets must match the number of elements (unless starred target is present). Starred target *name collects remaining elements into a list (not a tuple). At most one starred target per unpacking. Nested unpacking is supported: (a, b), c = (1, 2), 3. 3.6 Hashability tuple.__hash__ is defined only if all elements are hashable. Hash is computed from the hash of all elements using a combination formula. CPython: tuple.__hash__ is resistant to hash collision attacks (same randomization as other types). 4. Type Rules (Dunder Methods and Protocols) 4.1 Immutable Sequence Protocol object . __len__ ( self ) -> int # len(t)
object . __getitem__ ( self , key ) -> value # t[i], t[i:j]
object . __iter__ ( self ) -> iterator # iter(t), for x in t
object . __reversed__ ( self ) -> iterator # reversed(t)
object . __contains__ ( self , item ) -> bool # x in t
4.2 Concatenation and Repetition (Read-Only)
object . __add__ ( self , other ) -> tuple # t1 + t2 (returns new tuple)
object . __mul__ ( self , n ) -> tuple # t * n
object . __rmul__ ( self , n ) -> tuple # n * t
Note: No __iadd__ or __imul__ for tuples (unlike lists); t += (x,) rebinds t. 4.3 Hash Protocol object . __hash__ ( self ) -> int
# Defined for tuples iff all elements are hashable.
# If any element is unhashable (e.g., list), __hash__ raises TypeError.
4.4 Comparison Protocol
# Tuples compare lexicographically.
object . __lt__ ( self , other )
object . __le__ ( self , other )
object . __eq__ ( self , other )
object . __ne__ ( self , other )
object . __gt__ ( self , other )
object . __ge__ ( self , other )
Comparison is element-by-element; the first differing pair determines the result. 5. Behavioral Specification 5.1 tuple() Constructor tuple() → () tuple(iterable) → tuple containing all elements of the iterable, in iteration order. Consumes the iterable fully. 5.2 Tuple Methods (Only Two) Method Description Complexity count(x) Returns number of occurrences of x O(n) index(x[, start[, stop]]) Returns index of first x; raises ValueError if absent O(n)
Tuples are slightly faster to create and iterate than lists (CPython). Tuples use less memory: no over-allocation; sys.getsizeof((1,2,3)) < sys.getsizeof([1,2,3]). Tuples have __hash__ (if all elements hashable); lists do not. Tuple literals may be folded into constants at compile time (CPython peephole optimizer). 5.4 Tuple Unpacking Semantics Evaluate the right-hand side to get an iterable. If the target is a plain tuple/list of names: check lengths match; bind each. If a starred target is present: bind the starred name to a list of remaining items. Nested targets are resolved recursively. 5.5 namedtuple and typing.NamedTuple namedtuple creates a tuple subclass with named fields. Fields are accessible by index (inherited from tuple) or by name (attribute access). _make(iterable), _asdict(), _replace(**kwargs), _fields class attribute. typing.NamedTuple adds type annotations and is preferred in modern code. 6. Defined vs Undefined Behavior 6.1 Defined t[i] = v always raises TypeError — tuples are immutable. del t[i] always raises TypeError. tuple() == () is True. (1,) == (1,) is True. t * 0 → (). t * n for negative n → (). Lexicographic comparison: (1, 2) < (1, 3) is True. hash((1, 2, 3)) is defined and stable within a process. 6.2 Undefined / Implementation-Defined Exact hash value: hash((1, 2, 3)) is randomized per process (since Python 3.3 PYTHONHASHSEED randomization affects tuples through their elements). CPython compile-time folding: CPython may fold (1, 2) + (3, 4) into (1, 2, 3, 4) as a constant (peephole optimizer). This is not guaranteed. 7. Edge Cases from the Spec (CPython-Specific Notes) 7.1 Single-Element Tuple a = ( 42 ) # int 42 — NOT a tuple
b = ( 42 ,) # tuple with one element
c = 42 , # also a tuple!
print ( type ( a )) # <class 'int'>
print ( type ( b )) # <class 'tuple'>
print ( type ( c )) # <class 'tuple'>
7.2 Mutable Element in Tuple t = ( 1 , [ 2 , 3 ], 4 )
t [ 1 ] . append ( 5 ) # valid! mutates the list inside the tuple
print ( t ) # (1, [2, 3, 5], 4)
# But the tuple is still not hashable:
try :
hash ( t )
except TypeError as e :
print ( e ) # unhashable type: 'list'
7.3 Starred Unpacking Returns a list first , * rest = ( 1 , 2 , 3 , 4 , 5 )
print ( type ( rest )) # <class 'list'> — NOT a tuple!
print ( rest ) # [2, 3, 4, 5]
7.4 Tuple += Rebinds the Variable t = ( 1 , 2 )
original_id = id ( t )
t += ( 3 ,) # creates a NEW tuple; rebinds t
print ( id ( t ) == original_id ) # False
print ( t ) # (1, 2, 3)
7.5 Nested Unpacking ( a , b ), ( c , d ) = ( 1 , 2 ), ( 3 , 4 )
print ( a , b , c , d ) # 1 2 3 4
# In for loops:
pairs = [( 1 , 'a' ), ( 2 , 'b' ), ( 3 , 'c' )]
for num , letter in pairs :
print ( f " { num } : { letter } " )
7.6 Tuple Comprehension Does Not Exist # There is no tuple comprehension syntax.
# (x for x in range(5)) is a GENERATOR EXPRESSION, not a tuple.
gen = ( x for x in range ( 5 ))
print ( type ( gen )) # <class 'generator'>
# To get a tuple from a comprehension:
t = tuple ( x ** 2 for x in range ( 5 ))
print ( t ) # (0, 1, 4, 9, 16)
7.7 typing.NamedTuple Example from typing import NamedTuple
class Point ( NamedTuple ):
x : float
y : float
label : str = "point"
p = Point ( 1.0 , 2.0 )
print ( p . x , p . y ) # 1.0 2.0
print ( p . _asdict ()) # {'x': 1.0, 'y': 2.0, 'label': 'point'}
print ( p . _replace ( x = 5.0 )) # Point(x=5.0, y=2.0, label='point')
print ( p [ 0 ]) # 1.0 (still a tuple by index)
8. Version History (PEPs and Python Versions) Feature PEP Version tuple built-in — Python 1.0 collections.namedtuple — Python 2.6 Extended unpacking (*rest) PEP 3132 Python 3.0 typing.NamedTuple PEP 526 Python 3.6 typing.NamedTuple with class syntax — Python 3.6 namedtuple defaults= parameter — Python 3.6.1 * unpacking in tuple/list/set displays PEP 448 Python 3.5 tuple[int, str] subscript (generic) PEP 585 Python 3.9 tuple[int, ...] in typing PEP 484 Python 3.5
9. Implementation-Specific Behavior 9.1 CPython Memory Layout CPython stores tuple elements as a C array of PyObject* pointers. sys.getsizeof(()) = 40 bytes; each additional element adds 8 bytes (64-bit). List: sys.getsizeof([]) = 56 bytes + over-allocation. Tuple is always exactly the right size. 9.2 CPython Tuple Interning CPython interns the empty tuple () as a singleton. Small tuples (0-2 elements) may be cached in a "free list" for performance. () is () is True in CPython (same object). (1,) is (1,) may be True or False depending on context (compile-time constant folding). 9.3 CPython Peephole Optimizer Tuple literals composed of constants are folded at compile time. (1, 2, 3) in a function body becomes a single LOAD_CONST instruction. (1, 2) + (3, 4) may be folded to (1, 2, 3, 4) at compile time. 9.4 PyPy PyPy uses similar tuple semantics but may differ in interning and free list behavior. Strategy-based storage: tuples of uniform type may use compact storage. 10. Spec Compliance Checklist 11. Official Examples (Runnable Python 3.10+) from typing import NamedTuple
from collections import namedtuple
# ----------------------------------------------------------------
# 1. Tuple creation
# ----------------------------------------------------------------
empty = ()
single = ( 42 ,) # MUST have trailing comma
double = ( 1 , 2 )
mixed = ( 1 , "two" , 3.0 , [ 4 , 5 ])
no_paren = 1 , 2 , 3 # implicit tuple
print ( type ( single )) # <class 'tuple'>
print ( type (( 42 ))) # <class 'int'> — NOT a tuple!
# ----------------------------------------------------------------
# 2. Indexing and slicing (read-only)
# ----------------------------------------------------------------
t = ( 10 , 20 , 30 , 40 , 50 )
print ( t [ 0 ]) # 10
print ( t [ - 1 ]) # 50
print ( t [ 1 : 4 ]) # (20, 30, 40)
print ( t [:: - 1 ]) # (50, 40, 30, 20, 10)
# Immutability:
try :
t [ 0 ] = 99
except TypeError as e :
print ( e ) # 'tuple' object does not support item assignment
# ----------------------------------------------------------------
# 3. Unpacking
# ----------------------------------------------------------------
a , b , c = ( 1 , 2 , 3 )
print ( a , b , c ) # 1 2 3
first , * middle , last = ( 1 , 2 , 3 , 4 , 5 )
print ( first , middle , last ) # 1 [2, 3, 4] 5
print ( type ( middle )) # <class 'list'>
# Nested unpacking:
( x , y ), z = ( 1 , 2 ), 3
print ( x , y , z ) # 1 2 3
# Swap:
a , b = b , a
print ( a , b ) # 2 1
# ----------------------------------------------------------------
# 4. Tuple as dict key (hashable)
# ----------------------------------------------------------------
grid = {( 0 , 0 ): "origin" , ( 1 , 0 ): "right" , ( 0 , 1 ): "up" }
print ( grid [( 0 , 0 )]) # origin
print ( grid [( 1 , 0 )]) # right
# List as key would raise TypeError:
try :
d = {[ 1 , 2 ]: "value" }
except TypeError as e :
print ( e ) # unhashable type: 'list'
# ----------------------------------------------------------------
# 5. Tuple concatenation and repetition
# ----------------------------------------------------------------
t1 = ( 1 , 2 , 3 )
t2 = ( 4 , 5 , 6 )
print ( t1 + t2 ) # (1, 2, 3, 4, 5, 6)
print ( t1 * 3 ) # (1, 2, 3, 1, 2, 3, 1, 2, 3)
# += rebinds (does not mutate):
t = ( 1 ,)
original_id = id ( t )
t += ( 2 ,)
print ( t ) # (1, 2)
print ( id ( t ) == original_id ) # False
# ----------------------------------------------------------------
# 6. Tuple methods
# ----------------------------------------------------------------
t = ( 1 , 2 , 3 , 2 , 1 , 2 )
print ( t . count ( 2 )) # 3
print ( t . index ( 3 )) # 2
print ( t . index ( 2 , 3 )) # 3 — first 2 from index 3 onward
# ----------------------------------------------------------------
# 7. Mutable element inside tuple
# ----------------------------------------------------------------
t = ([ 1 , 2 ], [ 3 , 4 ])
t [ 0 ] . append ( 5 ) # valid — mutates the list
print ( t ) # ([1, 2, 5], [3, 4])
# Unhashable due to list element:
try :
hash ( t )
except TypeError as e :
print ( e ) # unhashable type: 'list'
# ----------------------------------------------------------------
# 8. Generator expression vs tuple
# ----------------------------------------------------------------
gen = ( x ** 2 for x in range ( 5 )) # generator, NOT tuple
print ( type ( gen )) # <class 'generator'>
tpl = tuple ( x ** 2 for x in range ( 5 )) # materialize to tuple
print ( tpl ) # (0, 1, 4, 9, 16)
# ----------------------------------------------------------------
# 9. collections.namedtuple
# ----------------------------------------------------------------
Point = namedtuple ( "Point" , [ "x" , "y" ])
p = Point ( 3 , 4 )
print ( p . x , p . y ) # 3 4
print ( p [ 0 ], p [ 1 ]) # 3 4 (index access still works)
print ( p . _asdict ()) # {'x': 3, 'y': 4}
print ( p . _replace ( x = 10 )) # Point(x=10, y=4)
print ( Point . _fields ) # ('x', 'y')
# ----------------------------------------------------------------
# 10. typing.NamedTuple with annotations and defaults
# ----------------------------------------------------------------
class Employee ( NamedTuple ):
name : str
department : str
salary : float = 50_000.0
emp = Employee ( "Alice" , "Engineering" )
print ( emp ) # Employee(name='Alice', department='Engineering', salary=50000.0)
print ( emp . name ) # Alice
print ( emp . _asdict ()) # OrderedDict(...)
print ( isinstance ( emp , tuple )) # True
# ----------------------------------------------------------------
# 11. Lexicographic comparison
# ----------------------------------------------------------------
print (( 1 , 2 ) < ( 1 , 3 )) # True
print (( 1 , 2 ) < ( 2 , 0 )) # True
print (( 1 , 2 , 3 ) > ( 1 , 2 )) # True (longer tuple is greater if prefix equal)
print (( 1 , 2 ) == ( 1 , 2 )) # True
print (( 1 , 2 ) != ( 1 , 3 )) # True
# ----------------------------------------------------------------
# 12. Tuple generic type hints (Python 3.9+)
# ----------------------------------------------------------------
def distance ( p : tuple [ float , float ]) -> float :
import math
return math . sqrt ( p [ 0 ] ** 2 + p [ 1 ] ** 2 )
print ( distance (( 3.0 , 4.0 ))) # 5.0
# Variable-length homogeneous tuple:
def sum_coords ( coords : tuple [ float , ... ]) -> float :
return sum ( coords )
print ( sum_coords (( 1.0 , 2.0 , 3.0 ))) # 6.0
Section Topic URL §3.2 Immutable sequence types https://docs.python.org/3/reference/datamodel.html#immutable-sequence-types §6.2.3 Parenthesized forms https://docs.python.org/3/reference/expressions.html#parenthesized-forms §7.2 Assignment / unpacking https://docs.python.org/3/reference/simple_stmts.html#assignment-statements tuple Built-in type https://docs.python.org/3/library/stdtypes.html#tuple Sequence operations Common operations https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range collections.namedtuple Named tuple factory https://docs.python.org/3/library/collections.html#collections.namedtuple typing.NamedTuple Typed named tuple https://docs.python.org/3/library/typing.html#typing.NamedTuple PEP 3132 Extended unpacking (*rest) https://peps.python.org/pep-3132/ PEP 448 Unpacking generalizations https://peps.python.org/pep-0448/ PEP 484 Type hints for tuples https://peps.python.org/pep-0484/ PEP 526 Variable annotations https://peps.python.org/pep-0526/ PEP 585 tuple[int, str] subscript https://peps.python.org/pep-0585/
In this topic