Python Tuples, Sets and Immutability
AI-assistedDrafted with AI assistance and reviewed before publishing; examples are version-pinned. Verify against official docs when in doubt.
Lists and dicts do most of the work, but two other containers remove a lot of code: tuples express fixed records, and sets answer “have I seen this?” in constant time.
Tuples: small fixed records
point = (3, 4)
x, y = point # unpacking
print(x, y) # 3 4
Parentheses are optional; the comma makes the tuple:
a = 1, 2 # (1, 2)
single = (1,) # trailing comma required
not_a_tuple = (1) # just the integer 1
Use a tuple when the number of fields is fixed and their order means something: coordinates, database rows, (key, value) pairs.
Packing and unpacking
name, age = "Ada", 36
first, *rest = [1, 2, 3, 4] # first=1, rest=[2, 3, 4]
a, b = b, a # swap without a temporary
Returning several values from a function is just returning a tuple:
def min_max(values):
return min(values), max(values)
low, high = min_max([3, 1, 9])
Named tuples
point[0] says nothing about what it holds. NamedTuple adds field names while staying a tuple:
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
p = Point(3, 4)
p.x # 3
p._asdict() # {'x': 3, 'y': 4}
It still unpacks, compares by value, and works as a dictionary key.
What immutability actually means
A tuple cannot gain, lose, or replace elements. Its contents can still be mutable:
record = (1, [2, 3])
record[1].append(4) # fine: mutating the list
record[1] = [2, 3, 4] # TypeError: cannot rebind the slot
Hashability follows from the elements: a tuple is hashable only if every element is.
{(1, 2): "ok"} # fine
{(1, [2]): "ok"} # TypeError: unhashable type: 'list'
So a tuple is a reliable dict key only when it holds immutable values.
Sets: deduplication and membership
tags = {"python", "web", "python"}
print(tags) # {'python', 'web'}
Sets are unordered and cannot be indexed. Their value is the average O(1) membership test:
if "python" in tags: # O(1) on a set, O(n) on a list
print("present")
Add and remove:
tags.add("cli")
tags.discard("web") # no error if absent
tags.remove("web") # KeyError if absent
Set operations
a = {"python", "go"}
b = {"go", "rust"}
a | b # union: {'python', 'go', 'rust'}
a & b # intersection: {'go'}
a - b # difference: {'python'}
a ^ b # symmetric: {'python', 'rust'}
Deduplicating while preserving order needs a dict, because sets do not keep insertion order:
items = ["b", "a", "b", "c"]
list(dict.fromkeys(items)) # ['b', 'a', 'c']
frozenset is the immutable, hashable variant:
frozen = frozenset(["a", "b"])
{frozen: "usable as a key"}
Choosing a container
| Need | Container | Why |
|---|---|---|
| Ordered, mutable, duplicates allowed | list |
index access, append |
| Fixed record, usable as a key | tuple / NamedTuple |
immutable, hashable |
| Deduplication, fast membership | set |
O(1) lookup |
| Key-value mapping | dict |
lookup by key |
A useful rule: if the code says x in collection inside a loop, a set or dict is usually the right structure.
Common mistakes
1. Forgetting the comma in a one-element tuple
("a") is a string; ("a",) is a tuple. This bites when building tuples dynamically.
2. Depending on set order
set(["c", "a"]) has no guaranteed iteration order. Sort it before displaying or comparing output.
3. Putting a list inside a set or dict key
{["a"]: 1} # TypeError: unhashable type: 'list'
Convert to a tuple or frozenset.
4. Writing {} for an empty set
{} # empty dict
set() # empty set
5. Assuming a tuple makes everything inside immutable
A tuple containing a list still allows mutation of that list — and becomes unhashable.
6. Using a mutable default value
The classic bug belongs to functions, but tuples do not protect you: if the tuple holds a list and you use it as a default argument, the list is shared across calls.
Summary
- Tuples are defined by commas, support unpacking, and work as keys when all elements are hashable.
- Immutability blocks rebinding, not mutation of nested objects.
- Sets deduplicate and answer membership in O(1), at the cost of ordering.
dict.fromkeysdeduplicates while preserving order.- Pick the container from the operation: sequence, record, membership, or mapping.
Next: functions, arguments, and how values cross the call boundary.