On this page
Python tuples are ordered, immutable sequences, written with commas and usually parentheses: (3, 4). Their immutability is the point, not a limitation: it is what lets a tuple act as a dictionary key or set member, which a list never can. The catch is that (5) is not a tuple at all, and a tuple holding a list is not truly immutable.
This is the hands-on guide to tuples themselves, for readers who already know lists. You will learn to create and unpack them, key dictionaries on them, switch to named tuples when positions get unreadable, and avoid the five mistakes listed near the end. If you are only deciding between the two sequence types, lists vs tuples in Python is the quicker read.
How do you create Python tuples?
point = (3, 4)
rgb = (255, 128, 0)
empty = ()
mixed = (1, "two", 3.0, [4, 5])
# parentheses are optional
coords = 3, 4
# from any iterable
tuple("abc") # ('a', 'b', 'c')
tuple([1, 2]) # (1, 2)
The gotcha that catches everyone:
not_a_tuple = (5) # this is the integer 5
actual_tuple = (5,) # this is a tuple - note the comma
type(not_a_tuple) # <class 'int'>
type(actual_tuple) # <class 'tuple'>
The comma creates the tuple, not the parentheses. The official tutorial on tuples and sequences describes the trailing comma as ugly but effective. It is worth saying twice, because the resulting bugs are quiet: a function that returns (value) instead of (value,) returns a different type than its caller expects.
How does tuple unpacking work?
Unpacking assigns each element of a tuple to its own name in one statement, as in x, y = point. It is the tuple feature you will use most, often without thinking of it as tuples:
point = (3, 4)
x, y = point
# swapping, no temporary variable
a, b = 1, 2
a, b = b, a
# functions returning multiple values return a tuple
def min_max(nums):
return min(nums), max(nums)
low, high = min_max([3, 1, 4, 1, 5]) # low = 1, high = 5
In fact, you already unpack tuples every time you loop over enumerate() or dict.items(), because both yield pairs:
list(enumerate("xy")) # [(0, 'x'), (1, 'y')]
list({"a": 1}.items()) # [('a', 1)]
Extended unpacking with a star collects the remainder into a list:
first, *rest = (1, 2, 3, 4, 5)
# first = 1, rest = [2, 3, 4, 5]
first, *middle, last = (1, 2, 3, 4, 5)
# first = 1, middle = [2, 3, 4], last = 5
_, important, _ = ("skip", "keep", "skip")
a, b = (1, 2), (3,)
(*a, *b, 4) # (1, 2, 3, 4) - star-unpacking into a new tuple
Nested unpacking works too, which is handy when iterating structured data:
records = [("Ananya", (85, 92)), ("Rohit", (78, 88))]
for name, (maths, science) in records:
print(f"{name}: {maths + science}")
Ananya: 177
Rohit: 166
Why does tuple immutability matter?
Tuples are hashable, so they can be dictionary keys and set members. Lists cannot. The glossary definition of hashable adds the condition: an immutable container is hashable only if its elements are hashable too.
locations = {
(19.0760, 72.8777): "Mumbai",
(28.6139, 77.2090): "Delhi",
}
print(locations[(19.0760, 72.8777)]) # Mumbai
# with a list as the key this raises TypeError (unhashable type: 'list')
This is genuinely useful for keying on compound values, such as a (user_id, date) pair, a grid coordinate or a category combination.
from collections import defaultdict
sales = defaultdict(int)
sales[("North", "Q1")] += 1200
sales[("North", "Q2")] += 1450
As a worked example, Counter can count combinations directly when each combination is a tuple:
from collections import Counter
orders = [("Pune", "Laptop"), ("Delhi", "Phone"),
("Pune", "Laptop"), ("Pune", "Phone")]
Counter(orders).most_common(2)
# [(('Pune', 'Laptop'), 2), (('Delhi', 'Phone'), 1)]
Tuples also signal intent. A reader seeing a tuple knows the contents are not expected to change. That is documentation the language enforces.
Can the contents of a tuple change?
Yes, if an element is itself mutable. A tuple's immutability is shallow: it fixes which objects the tuple holds, not the state of those objects.
t = (1, 2, [3, 4])
t[2].append(5)
print(t) # (1, 2, [3, 4, 5])
The tuple itself is unchanged, because it still references the same three objects. But the list inside is mutable, and nothing stops you modifying it.
The practical consequence is that such a tuple is not hashable either:
hash((1, 2, [3, 4])) # TypeError: unhashable type: 'list'
hash((1, 2, (3, 4))) # fine - returns an int
The strangest version of this is += on a list inside a tuple. It raises an error and changes the list, a case the Python FAQ explains under why a_tuple[i] += ['item'] raises an exception when the addition works:
t = (1, [2])
t[1] += [3]
# TypeError: 'tuple' object does not support item assignment
print(t) # (1, [2, 3]) - the list was extended anyway
If you need a genuinely immutable nested structure, therefore, use tuples all the way down.
When should you switch to a named tuple?
Switch as soon as positional access stops explaining itself: person[2] tells a reader nothing, while person.city does. A named tuple adds field names without giving up any tuple behaviour.
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"])
p = Person("Ananya", 28, "Pune")
print(p.name) # Ananya - readable
print(p[0]) # Ananya - still works positionally
print(p) # Person(name='Ananya', age=28, city='Pune')
They remain tuples, so unpacking and hashing still work. Moreover, the collections.namedtuple documentation notes that instances have no per-instance dictionary and need no more memory than regular tuples.
The typed version, typing.NamedTuple, reads better in modern code:
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
def distance_from_origin(self) -> float:
return (self.x ** 2 + self.y ** 2) ** 0.5
p = Point(3, 4)
p.distance_from_origin() # 5.0
Useful methods:
p._asdict() # {'x': 3, 'y': 4}
p._replace(x=10) # Point(x=10, y=4) - returns a new instance
If you need mutability or more behaviour, a @dataclass is the natural next step — see our OOP guide.
Tuple methods and operations
Tuples have only two methods, because there is nothing to modify:
t = (1, 2, 2, 3, 2)
t.count(2) # 3
t.index(2) # 1 - first occurrence
Everything else is standard sequence behaviour, summarised here:
| Operation | Example | Result |
|---|---|---|
| Length | len(t) |
5 |
| Concatenation | t + (4, 5) |
a new tuple |
| Repetition | (0,) * 3 |
(0, 0, 0) |
| Membership | 3 in t |
True |
| Slicing | t[1:3] |
(2, 2) |
| Sorting | sorted(t) |
a list: [1, 2, 2, 2, 3] |
| Comparison | (1, 2, 3) < (1, 3) |
True - compared item by item |
Note that t + (4, 5) builds a new tuple. Consequently, repeatedly concatenating in a loop is O(n²) — use a list to accumulate and convert at the end.
Common mistakes with Python tuples
- Forgetting the trailing comma in a one-element tuple.
- Assuming a tuple is deeply immutable when it holds a list or dict.
- Using a tuple containing a list as a dict key, which raises
TypeError. - Growing a tuple in a loop with
+, instead of appending to a list and callingtuple()once. - Relying on positions like
row[7]when a named tuple would say what the field is.
When should you use a tuple?
Use a tuple for fixed-size heterogeneous records, multiple return values, dictionary keys and set members, and anywhere you want to signal that something does not change.
Use a list for homogeneous collections that grow, shrink or get sorted in place.
The lists vs tuples comparison goes deeper on that decision, including memory and the mutable default argument bug.
The Sunday Growth Brief
One email a week: the best new comparisons, a fresh roadmap and the tech news worth your attention.
No spam. Unsubscribe in one click.
Related reading
Lists and tuples compared covers when to use each. Python basics covers the wider set of core types.
Frequently asked questions
What is the difference between a tuple and a list?
Lists are mutable and unhashable; tuples are immutable and hashable, provided their contents are hashable. That single difference drives everything else: tuples can be dictionary keys, they are slightly smaller, and they signal to a reader that the contents are not meant to change. Our lists vs tuples comparison covers when to choose each.
Why does (5) not create a tuple?
Because the parentheses are just grouping, so (5) is the integer 5. The comma creates the tuple, not the brackets. Write (5,) instead, or even 5, on its own, which is also a valid one-element tuple. The empty tuple () is the only tuple that needs no comma at all.
Can I modify a tuple in Python?
No, you cannot rebind its elements, so t[0] = 1 raises TypeError. However, immutability is shallow: if a tuple contains a mutable object such as a list, that object can still change in place. To get a modified tuple, build a new one, for example with concatenation or a named tuple's _replace method.
Are tuples faster than lists?
Slightly, for creation, and they use a little less memory because they never over-allocate. Iteration speed is essentially the same. The difference rarely matters in real programs, so choose based on whether the data should be immutable or needs to act as a dictionary key, not on performance.
Written by
Ayushi Kulshreshta
AI Engineer
AI engineer building retrieval-augmented systems, previously a software developer at VDB Inc. and an R&D associate at Nokia.

