Skip to content
1stepGrowLearnCompareGrow

Python Tuples: Unpacking, Dict Keys and the Trailing-Comma Trap

How to use Python tuples well: creating them, unpacking, using them as dict keys, named tuples, and the one-element and += gotchas that catch everybody.

Ayushi Kulshreshta

AI Engineer

9 min readUpdated
Share
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?

Python
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:

Python
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:

Python
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:

Python
list(enumerate("xy"))       # [(0, 'x'), (1, 'y')]
list({"a": 1}.items())      # [('a', 1)]

Extended unpacking with a star collects the remainder into a list:

Python
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:

Python
records = [("Ananya", (85, 92)), ("Rohit", (78, 88))]
for name, (maths, science) in records:
    print(f"{name}: {maths + science}")
text
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.

Python
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.

Python
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:

Python
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.

Python
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:

Python
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:

Python
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.

Python
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:

Python
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:

Python
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:

Python
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 calling tuple() 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.

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.

Retrieval-Augmented GenerationLangChainPythonMachine learning

Related reading

Free weekly newsletter

Get the shortlist before everyone else

Every Sunday we send one email with the week’s sharpest course comparison, a career roadmap worth stealing, and the tech news and hiring signals we’re watching.

  • No fluff, ever
  • Unsubscribe anytime
  • 5-minute read

We never share your address. One click to leave.