Home
Back to Blogs
What Is a Tuple in Python?

What Is a Tuple in Python?

Murat Hüdavendigâr Öncü
December 13, 2022
4 min
#python#programming#data-structures#coding-basics#backend

A beginner-friendly guide to Python tuples, exploring immutability, packing, unpacking, and exactly how they differ from lists.

In our previous article, we talked about lists in python. If you haven’t read it yet, you can read it by clicking here.

After understanding lists, the next fundamental data structure to master in Python is the tuple. At first glance, a tuple looks and behaves very similarly to a list. However, there is one massive difference that dictates exactly when and why you should use them.

What is a Tuple?

A tuple is a collection of items that is ordered and unchangeable (immutable). Once you create a tuple, you cannot add, remove, or change its elements. You define a tuple by placing your items inside parentheses (), separated by commas.

# A standard tuple
coordinates = (10, 20)

# A tuple with mixed data types
user_info = ("Murat", 28, True)

# A tuple with a single item (note the trailing comma)
single_item_tuple = ("apple",)

Note: The trailing comma in a single-item tuple is mandatory. Without it, Python will simply treat the parentheses as a mathematical expression, and the variable will be evaluated as a simple string or integer rather than a tuple.

Why Use Tuples Instead of Lists?

You might be wondering: if tuples are just lists that I cannot change, why would I ever use them?

  1. Data Integrity: If you have data that should never be altered throughout the lifecycle of your program (like configuration settings, days of the week, or geographic coordinates), putting them in a tuple guarantees that a bug in your code cannot accidentally modify them.

  2. Performance and Memory: Because tuples are immutable, Python allocates a fixed block of memory for them. This makes them slightly faster and more memory-efficient than lists, especially when dealing with large amounts of static data.

  3. Dictionary Keys: Because they are immutable, tuples are hashable. This means you can use a tuple as a key in a Python dictionary, which is something you cannot do with a mutable list.

Accessing Tuple Elements

Just like lists, tuples are ordered, meaning you can access their items using zero-based indexing and negative indexing. You can also use slicing to extract a portion of the tuple.

colors = ("red", "green", "blue", "yellow")

print(colors[1])   # Output: green
print(colors[-1])  # Output: yellow

# Slicing
print(colors[1:3]) # Output: ('green', 'blue')

If you try to modify an element, Python will immediately throw a TypeError to protect the data structure.

colors[0] = "black" # Raises TypeError: 'tuple' object does not support item assignment

Packing and Unpacking

One of the most powerful and common features of tuples in Python is packing and unpacking. When you create a tuple, you are 'packing' values into it. You can easily 'unpack' those values back into individual variables in a single line of code.

# Packing
dimensions = (1920, 1080)

# Unpacking
width, height = dimensions

print(width)  # Output: 1920
print(height) # Output: 1080

This technique is heavily used when returning multiple values from a function. A function that returns multiple items separated by commas is actually returning a packed tuple under the hood.

Built-in Tuple Methods

Because tuples cannot be modified, they only have two built-in methods:

  • count(): Returns the number of times a specified value appears in the tuple.
  • index(): Searches the tuple for a specified value and returns its position.
numbers = (1, 3, 7, 8, 7, 5, 4, 6, 8, 5)

print(numbers.count(5))  # Output: 2
print(numbers.index(8))  # Output: 3 (returns the first occurrence)

Extended Unpacking with the Asterisk

Basic unpacking requires the number of variables on the left to exactly match the number of items in the tuple. When you only care about some of the values — "give me the first item, and everything else separately" — the * operator captures the remainder into a list:

scores = (95, 88, 76, 82, 91)

highest, *rest = scores
print(highest)  # 95
print(rest)     # [88, 76, 82, 91]

first, *middle, last = scores
print(first)   # 95
print(middle)  # [88, 76, 82]
print(last)    # 91

This comes up constantly when a function returns a fixed "header" value followed by a variable-length "body" — for example, a CSV row where the first column is always an ID and the rest are variable data columns.

Where Tuples Show Up Without You Creating Them Directly

A lot of tuple usage in real Python code isn't explicit — it comes from language features that hand you tuples implicitly. Looping over a dictionary's .items() unpacks a tuple on every iteration:

grades = {"Math": 90, "Science": 85}

for subject, score in grades.items():  # each iteration unpacks a (key, value) tuple
    print(f"{subject}: {score}")

enumerate() does the same thing, yielding (index, item) tuples:

for index, fruit in enumerate(["apple", "banana"]):  # unpacks an (index, item) tuple
    print(index, fruit)

Recognizing that both of these are tuple unpacking under the hood — not special loop syntax — makes it much easier to predict how similar patterns will behave elsewhere in Python.

Named Tuples: Tuples with Labeled Fields

Plain tuples are positional — user_info[1] tells you nothing about what that value represents without checking how the tuple was built. collections.namedtuple (and the newer, type-annotated typing.NamedTuple) solves this by giving each position a name, while keeping all the immutability and performance benefits of a regular tuple:

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int

origin = Point(0, 0)
p1 = Point(x=10, y=20)

print(p1.x)       # 10 — access by name
print(p1[0])      # 10 — still works positionally too
print(p1)         # Point(x=10, y=20) — readable repr

A named tuple is still a tuple — it's immutable, hashable, and supports indexing and unpacking exactly like the plain tuples above. What it adds is self-documenting field names, which makes code far easier to read months later compared to remembering that user_info[1] happens to be the age.

Concatenation, Repetition, and the Mutable-Contents Trap

Tuples support + (concatenation) and * (repetition), just like lists — both always produce a new tuple, since the originals can't be modified:

a = (1, 2)
b = (3, 4)

combined = a + b        # (1, 2, 3, 4)
repeated = a * 3         # (1, 2, 1, 2, 1, 2)

One subtlety trips up even experienced developers: a tuple's immutability only applies to the tuple's own slots — it doesn't make the objects inside it immutable. A tuple containing a list is immutable at the tuple level, but that inner list can still be mutated:

data = (1, 2, [3, 4])
data[2].append(5)
print(data)  # (1, 2, [3, 4, 5]) — the tuple didn't change, but the list inside it did

data[2] = [3, 4, 5, 6] would still raise a TypeError, because that's reassigning the tuple's slot — but data[2].append(...) mutates the list object the slot points to, which the tuple has no say over. If you need a collection that's genuinely immutable all the way down, every element inside it needs to be immutable too (tuples of tuples, or tuples of strings and numbers).

Tuple vs. List vs. Named Tuple: Which Should You Use?

StructureMutable?Best for
listYesCollections that grow, shrink, or get reordered
tupleNoFixed-size groups of related values that shouldn't change (coordinates, RGB, a function's multiple return values)
NamedTupleNoThe same use case as a tuple, but where labeled fields make the code significantly more readable

Common Pitfalls with Tuples

  • Forgetting the trailing comma for a single-item tuple. As noted above, ("apple") is just the string "apple" in parentheses — you need ("apple",) for Python to treat it as a one-element tuple. This is one of the most common early Python bugs and one of the hardest to spot by eye.
  • Assuming a tuple containing mutable objects is fully immutable. As shown above, immutability protects the tuple's structure (you can't reassign a slot or resize it), not the mutability of whatever objects live inside it.
  • Using a tuple when the data will actually need to change. If you find yourself needing to "modify" a tuple, the typical workaround is rebuilding a new tuple from scratch (t = t[:2] + (new_value,) + t[3:]), which is clumsy compared to just using a list from the start. Reach for a tuple because the data is conceptually fixed, not as a default choice for every ordered collection.

Conclusion

Tuples are the sturdy, reliable sibling of the Python list. While they lack the dynamic flexibility of lists, their immutability brings safety, performance benefits, and unique capabilities like dictionary key hashing and named-tuple readability. Knowing when to lock down your data with a tuple instead of a list — and when a NamedTuple makes that data self-documenting — is a clear sign of a maturing Python developer.

Back to all posts