Tuples, Immutability, and Packing
Key Takeaways
- Tuples are ordered sequences, so they support indexing, slicing, len, iteration, and membership tests.
- Tuples are immutable, so assigning to an element or deleting one element raises TypeError.
- A one-item tuple requires a trailing comma, such as (value,), because parentheses alone do not create a tuple.
- Tuple packing and unpacking let Python group several values and assign them back to names position by position.
- A tuple is hashable only when all of its elements are hashable, which matters when using tuples as dictionary keys.
Tuple basics
A tuple is an ordered sequence, usually written with parentheses. Like a list or string, it supports indexing, negative indexing, slicing, len(), membership with in, and iteration in a for loop. The major difference is immutability: after a tuple is created, its element positions cannot be reassigned, inserted into, or removed from individually.
point = (10, 20, 30)
print(point[0]) # 10
print(point[-1]) # 30
print(point[1:3]) # (20, 30)
print(len(point)) # 3
print(20 in point) # True
Trying to change a tuple element raises TypeError.
point = (10, 20)
point[0] = 99 # TypeError
This does not mean the variable name is frozen. You can rebind the name to a different tuple; the original tuple object is what cannot change.
point = (10, 20)
point = (99, 20) # allowed: name now refers to a new tuple
The comma creates the tuple
A common PCEP trap is the one-item tuple. Parentheses are often used merely to group expressions, so (5) is just the integer 5. The trailing comma is what actually makes a tuple.
| Expression | Resulting type | Note |
|---|---|---|
(5) | int | Parentheses only group the expression |
(5,) | tuple | One-item tuple |
5, | tuple | One-item tuple without parentheses |
(5, 6) | tuple | Two-item tuple |
() | tuple | The empty tuple |
5 | int | Plain integer |
When reading code, look for the comma before assuming a value is a tuple. A snippet like x = (3) * 2 produces 6, but x = (3,) * 2 produces (3, 3).
Packing and unpacking
Packing happens when Python gathers values into a tuple. Unpacking assigns elements from a tuple or other iterable into separate names.
record = 'Ada', 36, 'Python' # packing (no parentheses needed)
name, age, language = record # unpacking
print(name) # Ada
The number of target names must match the number of values unless starred unpacking is used. PCEP usually tests the simple rule: two names need exactly two values, three names need exactly three values, or Python raises ValueError.
x, y = (1, 2) # works
x, y = (1, 2, 3) # ValueError: too many values to unpack
Swapping and starred unpacking
A useful unpacking pattern swaps values without a temporary variable. Python evaluates the right side first, packs those values into a tuple, then unpacks them into the left-side names.
left, right = 'L', 'R'
left, right = right, left
print(left, right) # R L
Starred unpacking collects extra values into a list:
first, *rest = (1, 2, 3, 4)
print(first) # 1
print(rest) # [2, 3, 4] (a list, not a tuple)
Immutable tuple, mutable contents
Tuple immutability applies to the tuple positions, not necessarily to the objects inside them. A tuple can contain a list: you cannot replace the list at that tuple position, but you can mutate the list itself.
box = ([1, 2], 'ok')
box[0].append(3)
print(box) # ([1, 2, 3], 'ok')
That distinction matters for dictionary keys and set elements. A tuple is hashable only if every element inside it is hashable. (1, 2) can be a dictionary key; ([1, 2], 3) cannot, because the list inside it is mutable and unhashable.
Tuple methods and use cases
Tuples have far fewer methods than lists; the only two are count(value) and index(value) (both reads, since there is nothing to mutate). Use tuples when the structure should be fixed: a coordinate, a small record, dictionary keys, or multiple values returned from a function. Use lists when the collection is meant to grow, shrink, or be reordered. Because tuples cannot change size, they are slightly faster and signal intent that the data is constant.
Conversions, concatenation, and exam traps
The PCEP exam often blends tuples with the other collection types, so know how they convert and combine. The tuple() constructor turns any iterable into a tuple, so tuple([1, 2, 3]) gives (1, 2, 3) and tuple('hi') gives ('h', 'i'); conversely list((1, 2)) gives [1, 2]. Tuples support + and * like other sequences: (1, 2) + (3,) yields (1, 2, 3) and (0,) * 3 yields (0, 0, 0), but you still cannot concatenate a tuple with a list.
A subtle augmented-assignment trap appears when a tuple holds a mutable element: writing t = ([1], 2); t[0] += [9] actually mutates the inner list to [1, 9] and then raises TypeError because the tuple rejects the rebinding — yet the list change has already happened, so the inner list ends up [1, 9] despite the error. Functions commonly return tuples implicitly: return a, b packs the two values into a tuple, and the caller can unpack with x, y = func(). Because membership and comparison work element by element, (1, 2) == (1, 2) is True and (1, 2) < (1, 3) is True.
When a question shows parentheses, slow down and decide first whether a comma is present, because that single character changes an integer into a tuple and changes the entire predicted output of the snippet.
Which expression creates a tuple containing exactly one integer?
What happens when this code runs? t = (1, 2); t[0] = 9
Which value can be used as a dictionary key because it is hashable?