I ignored tuples for a long time when I was learning Python. I figured, “why would I use something that’s basically a list but you can’t change it?” It wasn’t until I started working with functions that return multiple values and dictionary keys that needed to be hashable that I finally understood why tuples exist as their own distinct data type, not just as a limited version of a list.
What Is a Tuple?
A tuple is an ordered, immutable collection of items in Python. “Ordered” means the items maintain a defined sequence you can index into. “Immutable” means that once a tuple is created, you cannot add, remove, or change its elements in place.
coordinates = (10, 20)
print(coordinates)
print(type(coordinates))
Output:
(10, 20)
(class 'tuple')
Creating Tuples
The most common way to create a tuple is with parentheses and comma-separated values.
person = ("Ahmad", 21, "Lahore")
print(person)
Output:
('Ahmad', 21, 'Lahore')
You can also create a tuple without parentheses at all — it’s actually the comma that defines a tuple in Python, not the parentheses themselves.
point = 3, 4
print(point)
print(type(point))
Output:
(3, 4)
(class 'tuple')
This leads to one of the most common gotchas in the language: creating a single-element tuple requires a trailing comma.
not_a_tuple = (5)
actual_tuple = (5,)
print(type(not_a_tuple))
print(type(actual_tuple))
Output:
(class 'int')
(class 'tuple')
Without that trailing comma, Python just treats the parentheses as a grouping operator around a single integer, not as a tuple constructor.
You can also build a tuple explicitly using the tuple() constructor, which is especially useful when converting from another iterable.
letters = tuple("abc")
print(letters)
Output:
('a', 'b', 'c')
Accessing Tuple Elements
Tuples support indexing and slicing exactly like lists.
person = ("Ahmad", 21, "Lahore")
print(person[0])
print(person[-1])
print(person[0:2])
Output:
Ahmad
Lahore
('Ahmad', 21)
Why Tuples Are Immutable — And What That Actually Means Internally
Trying to modify a tuple in place raises an error immediately.
person = ("Ahmad", 21, "Lahore")
person[1] = 22
TypeError: 'tuple' object does not support item assignment
Internally, CPython implements tuples as a fixed-size array of pointers to the objects it contains, allocated once at creation time. Because the size and contents are fixed at creation, CPython can store tuples more compactly and access their elements slightly faster than the dynamically resizable array used for lists. This isn’t just a theoretical difference — you can measure it.
import sys
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
print(sys.getsizeof(my_list))
print(sys.getsizeof(my_tuple))
Typical output on CPython:
104
80
The tuple consistently uses less memory than the equivalent list, because lists reserve extra capacity internally to make future appends cheaper, while tuples never need that extra room since they can never grow.
It’s worth being precise about what “immutable” really guarantees, though: a tuple itself can’t be resized or have its elements reassigned, but if a tuple contains a mutable object, like a list, that inner object can still be changed.
data = (1, 2, [3, 4])
data[2].append(5)
print(data)
Output:
(1, 2, [3, 4, 5])
The tuple’s own structure — which objects it points to, and how many — never changed. Only the mutable list living inside it changed.
Tuple Unpacking
This is where tuples genuinely shine in everyday Python code. You can unpack a tuple’s values directly into separate variables in a single line.
person = ("Ahmad", 21, "Lahore")
name, age, city = person
print(name)
print(age)
print(city)
Output:
Ahmad
21
Lahore
This is exactly how functions that “return multiple values” actually work in Python — they return a single tuple, which the caller then unpacks.
def get_min_max(numbers):
return min(numbers), max(numbers)
lowest, highest = get_min_max([4, 8, 1, 9, 3])
print(lowest, highest)
Output:
1 9
You can also unpack with a “catch the rest” star operator, which I use constantly when processing rows of data with a known first or last field.
first, *middle, last = (1, 2, 3, 4, 5)
print(first)
print(middle)
print(last)
Output:
1
[2, 3, 4]
5
Common Tuple Operations
a = (1, 2, 3)
b = (4, 5, 6)
# Concatenation
print(a + b)
# Repetition
print(a * 2)
# Membership
print(3 in a)
# Length
print(len(a))
# Iteration
for item in a:
print(item)
Output:
(1, 2, 3, 4, 5, 6)
(1, 2, 3, 1, 2, 3)
True
3
1
2
3
Tuples also support the standard sequence methods .count() and .index(), though the method set is deliberately minimal compared to lists, since there’s no need for methods like .append() or .sort() on something immutable.
numbers = (1, 2, 2, 3, 2)
print(numbers.count(2))
print(numbers.index(3))
Output:
3
3
Named Tuples: A More Readable Alternative
Plain tuples work fine for small, obvious groupings, but accessing fields by position (person[1]) gets hard to read as data grows more complex. This is where collections.namedtuple comes in — I use it a lot in data-processing scripts.
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"])
ahmad = Person(name="Ahmad", age=21, city="Lahore")
print(ahmad.name)
print(ahmad.age)
print(ahmad)
Output:
Ahmad
21
Person(name='Ahmad', age=21, city='Lahore')
A namedtuple is still a real tuple underneath — it supports indexing, unpacking, and immutability exactly like a regular tuple — but it also gives you readable, named attribute access.
Why Tuples Matter for Dictionary Keys and Sets
Because tuples are immutable, and their contents (as long as those contents are themselves immutable) never change, Python can compute a stable hash value for them. That means tuples can be used as dictionary keys or set members, while lists cannot.
locations = {
(31.5204, 74.3587): "Lahore",
(24.8607, 67.0011): "Karachi",
}
print(locations[(31.5204, 74.3587)])
Output:
Lahore
Attempting the same thing with a list key fails immediately:
bad_dict = {[1, 2]: "value"}
TypeError: unhashable type: 'list'
This single property — hashability — is one of the most practically important reasons to reach for a tuple instead of a list in real code, especially when working with coordinate pairs, composite database keys, or memoization caches.
Real-World Use Cases
- Returning multiple values from a function, as shown earlier with
get_min_max. - Fixed records of data that shouldn’t change, like RGB color values:
RED = (255, 0, 0). - Dictionary keys representing composite identifiers, like
(user_id, session_id). - Function arguments packed and unpacked with
*args, which internally is just a tuple.
def log_event(*args):
print(args)
print(type(args))
log_event("login", "user_42", "2026-07-30")
Output:
('login', 'user_42', '2026-07-30')
(class 'tuple')
Performance Considerations
Tuples are generally slightly faster to create and iterate over than lists, and they use less memory, precisely because CPython can allocate them as a fixed-size block once, without reserving extra growth capacity. For read-only data that won’t change throughout a program’s life, especially large volumes of small fixed records, tuples are the more efficient choice.
Best Practices
- Use a tuple whenever the collection represents a fixed, logically related group of values that shouldn’t change, like coordinates or a database record.
- Use
namedtuple(or adataclasswithfrozen=Truefor more advanced needs) when field names would make the code significantly more readable. - Don’t force data into a tuple just because it’s “faster” if the data genuinely needs to grow or shrink — a list is the correct tool in that case.
- Always add the trailing comma when creating a single-element tuple.
Common Mistakes
The single-element tuple trap is the mistake I see most often:
sizes = (10)
print(type(sizes))
Output:
(class 'int')
Another mistake is assuming a tuple containing a list is fully immutable, and being surprised when that inner list changes as shown earlier — immutability applies only to the tuple’s own structure, not necessarily to the mutable objects it references.
FAQs
What’s the main difference between a tuple and a list? Tuples are immutable and use (), lists are mutable and use []. Tuples generally use less memory and are slightly faster for read-only data.
Can a tuple contain mutable elements? Yes. A tuple can hold lists, dictionaries, or other mutable objects, and those inner objects can still be modified even though the tuple’s own structure cannot.
When should I use a tuple instead of a list? When the data is fixed and won’t change size or contents, when you need a hashable collection for use as a dictionary key or set element, or when returning multiple values from a function.
Are tuples faster than lists? Generally yes, for creation, iteration, and memory footprint, because of their fixed-size internal representation — though the difference is usually only meaningful at large scale.
What is a namedtuple, and when should I use it? It’s a tuple subclass from the collections module that lets you access elements by name instead of only by index, improving readability for structured records.
Summary
Tuples aren’t just “lists you can’t edit” — they’re a deliberately immutable, hashable, memory-efficient sequence type built for fixed collections of related values. From unpacking multiple return values, to serving as dictionary keys, to representing structured records with namedtuple, tuples solve real problems that lists structurally can’t. Once I understood the hashability angle and the internal fixed-size representation, tuples stopped feeling like a limitation and started feeling like the right tool for a very specific, very common job.
References
- Python official documentation on tuples: https://docs.python.org/3/library/stdtypes.html#tuples
- Python tutorial on tuples and sequences: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
collections.namedtupledocumentation: https://docs.python.org/3/library/collections.html#collections.namedtuple- PEP 8 – Style Guide for Python Code: https://peps.python.org/pep-0008/