SythraOpen app

Tuples in Python

A Python tuple is an immutable, ordered sequence created with commas, e.g. (1, 2, 3). Tuples are used to return multiple values from a function, unpack values with a, b = b, a, and as dictionary keys since they're hashable — unlike lists.

Sythra

16 min read

XLinkedIn
Tuples in Python — cover illustration

Lists can hold anything and can be changed freely. Strings hold characters and can never be changed. A tuple sits exactly in the middle: a sequence like a list, but immutable like a string. Once created, it cannot change — a promise that Python rewards by letting tuples do things lists can't, like act as dictionary keys.

This page covers creating tuples, the classic one-element trap, the elegant tuple-swap, returning multiple values from a function, *args, zip() and enumerate(), and the DSU sorting pattern. Pair it with Lists in Python and Dictionaries in Python for how all three sequence types interact.

What you will learn

By the end you can:

  • Create tuples and avoid the one-element tuple trap
  • Swap variables and unpack sequences with tuple assignment
  • Return multiple values from a function using a tuple
  • Write functions that accept any number of arguments with *args
  • Use zip() and enumerate(), and use tuples as dictionary keys

Tuples are immutable

A tuple is created with a comma-separated list of values. Parentheses are optional but conventional:

t = 'a', 'b', 'c', 'd', 'e'       # valid, no parentheses
t = ('a', 'b', 'c', 'd', 'e')     # same thing, with parentheses

Index and slice a tuple exactly like a list:

t = ('a', 'b', 'c', 'd', 'e')
print(t[0])      # 'a'
print(t[1:3])    # ('b', 'c')

But try to change an element and Python refuses:

t[0] = 'A'
# TypeError: 'tuple' object does not support item assignment

You can build a brand-new tuple from pieces of the old one — you are constructing something new, not modifying anything:

t = ('A',) + t[1:]
print(t)   # ('A', 'b', 'c', 'd', 'e')

Three ways to create a tuple

t = tuple()          # empty tuple: ()
t = tuple('lupins')  # from a string: ('l', 'u', 'p', 'i', 'n', 's')
t = ('a', 'b', 'c')  # literal
t = 'a', 'b', 'c'    # no parentheses (works, less readable)

The one-element tuple trap

One of Python's most famous gotchas: a single value in parentheses is not automatically a tuple — it is just that value with parentheses around it doing nothing. A genuine one-element tuple needs a trailing comma:

t1 = ('a',)     # this IS a tuple
t2 = ('a')      # this is NOT a tuple — it's just a string
t3 = 'a',       # also a valid one-element tuple

Tuple assignment — the elegant swap

Swapping two variables traditionally needs a temporary variable:

temp = a
a = b
b = temp

Tuple assignment collapses this into one line:

a, b = b, a

The right side is fully evaluated first — both b and a are read and packaged up before anything is assigned. Only then are the values handed to the left side, in order. No temporary variable, ever. The number of variables on the left must match the number of values on the right:

a, b = 1, 2, 3
# ValueError: too many values to unpack (expected 2)

The right side doesn't have to be a tuple at all — any sequence works, including the return value of a function like .split():

addr = 'monty@python.org'
uname, domain = addr.split('@')
print(uname)    # monty
print(domain)   # python.org

Reads almost like English: "Split the address at the @ symbol, and give me the username and the domain."

Returning multiple values from a function

A function can only ever return one value — but if that value is a tuple, the caller can unpack several results at once. The built-in divmod() does exactly this:

t = divmod(7, 3)
print(t)   # (2, 1)

quot, rem = divmod(7, 3)
print(quot)    # 2
print(rem)     # 1

Write your own functions the same way — no parentheses required to build a tuple to return:

def min_max(t):
    return min(t), max(t)

lo, hi = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(lo)    # 1
print(hi)    # 9

Anytime a function naturally produces two or more related outputs, returning them together as a tuple is more honest than stuffing them into a list, and cleaner than calling the function twice.

Variable-length arguments: *args

Sometimes a function should accept any number of arguments. A parameter starting with * gathers all remaining arguments into a single tuple:

def printall(*args):
    print(args)

printall(1, 2.0, '3')
# (1, 2.0, '3')

This is called gathering. Scatter is the opposite — spreading a tuple back out into separate arguments when calling a function. Passing a tuple directly where two arguments are expected fails:

t = (7, 3)
divmod(t)
# TypeError: divmod expected 2 arguments, got 1

Put a * in front and Python scatters it into separate arguments:

divmod(*t)
# (2, 1)

Gather (*args in a function definition) and scatter (* when calling) are exact opposites. Combine *args with sum() to sum any number of arguments:

def sumall(*args):
    return sum(args)

sumall(1, 2, 3)         # 6
sumall(10, 20, 30, 40)  # 100

zip() and enumerate()

zip() pairs two or more sequences together, element by element, by position:

s = 'abc'
t = [0, 1, 2]
list(zip(s, t))
# [('a', 0), ('b', 1), ('c', 2)]

If the sequences have different lengths, zip() stops at the shorter one:

list(zip('Anne', 'Elk'))
# [('A', 'E'), ('n', 'l'), ('n', 'k')]
# 'Anne' has 4 letters, 'Elk' has 3 — only 3 pairs

Combine zip() with tuple assignment in a for loop to compare two sequences side by side with no manual indexing:

def has_match(t1, t2):
    for x, y in zip(t1, t2):
        if x == y:
            return True
    return False

enumerate() gives you both the index and the value while traversing a sequence:

for index, element in enumerate('abc'):
    print(index, element)
# 0 a
# 1 b
# 2 c

Reach for enumerate() anytime you catch yourself writing for i in range(len(t)): element = t[i]enumerate() does the same thing more cleanly.

Dictionaries and tuples together

Lists can't be dictionary keys because they're mutable and unhashable. Tuples can — this opens up genuinely useful designs. .items() returns key-value pairs as tuples:

d = {'a': 0, 'b': 1, 'c': 2}
t = list(d.items())
print(t)
# [('a', 0), ('b', 1), ('c', 2)]

Going the other direction, build a dictionary from a list of tuples with dict() — and combine it with zip() for a one-liner:

t = [('a', 0), ('b', 1), ('c', 2)]
d = dict(t)

d = dict(zip('abc', range(3)))
print(d)   # {'a': 0, 'b': 1, 'c': 2}

Because tuples are hashable, they work as dictionary keys — useful whenever you need to index by a combination of values. A phone directory keyed by (last name, first name):

directory = {}
directory['Cleese', 'John'] = '08700 100 222'
directory['Palin', 'Michael'] = '08700 100 222'

for last, first in directory:
    print(first, last, directory[last, first])

directory[last, first] is exactly equivalent to directory[(last, first)] — it's the comma that creates the tuple, not the parentheses.

Comparing tuples and the DSU sorting pattern

Tuples support <, >, ==, comparing element by element, left to right. The first pair that differs decides the result:

(0, 1, 2) < (0, 3, 4)          # True — 0==0, then 1<3, decided
(0, 1, 2000000) < (0, 3, 4)    # True — 2000000 never even checked

This is exactly how sorting a list of tuples works too — primarily by the first element, breaking ties with the second, and so on. This enables a powerful sorting technique called DSU: Decorate, Sort, Undecorate. Pair each element with its sort key, sort those pairs, then strip the keys back off:

def sort_by_length(words):
    t = []
    for word in words:
        t.append((len(word), word))    # decorate

    t.sort(reverse=True)               # sort

    res = []
    for length, word in t:             # undecorate
        res.append(word)
    return res

print(sort_by_length(['banana', 'fig', 'cherry', 'kiwi']))
# ['banana', 'cherry', 'kiwi', 'fig']

DSU is the go-to pattern whenever Python's default sort order isn't what you need — the decorate step lets you attach any sort key at all.

Choosing between strings, lists, and tuples

TypeMutable?Use when
strNoWorking with text; convert to a list if you need to edit characters
listYesAdding/removing/changing elements; building a collection gradually
tupleNoReturning multiple values, using a sequence as a dict key, or guaranteeing data won't be modified

Because tuples are immutable, they lack in-place methods like .sort(). But sorted() and reversed() both work fine on them — they return brand-new lists instead of modifying the original. Rule of thumb: if the collection changes over time, use a list; if it represents a fixed record or a compound dictionary key, use a tuple.

A preview: named tuples

Regular tuples are accessed by position — t[0], t[1] — which isn't always readable. namedtuple gives each position an actual name:

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)

print(p.x)    # 3
print(p.y)    # 4
print(p[0])   # 3 — still works by index too

A named tuple is still completely immutable, but its fields have readable names — the safety of a tuple with the readability of an object. Great for fixed-structure records like coordinates or RGB colors.

Common mistakes

  • Writing ('a') expecting a tuple — it's just a string; you need the trailing comma ('a',)
  • Trying to assign into a tuple like t[0] = 'A' — tuples are immutable
  • Passing a tuple where separate arguments are expected without unpacking it with *
  • Confusing a list of tuples with a list of lists — they print similarly but behave very differently
  • Forgetting that tuple comparison stops at the first differing element

Why this matters

Tuples show up constantly once you start writing real Python: function return values, dictionary keys, coordinates, and the gather/scatter mechanics behind flexible function signatures. Knowing exactly when immutability helps — and when it gets in your way — is what separates code that "happens to work" from code that is deliberately designed.

Common questions

What is a tuple in Python?

A tuple is an immutable, ordered sequence of values, created with commas, e.g. t = (1, 2, 3). Once created, its elements cannot be changed, unlike a list.

How do you create a one-element tuple in Python?

You need a trailing comma: t = ('a',). Without the comma, ('a') is just the string 'a' in parentheses, not a tuple.

How do you swap two variables in Python without a temporary variable?

Use tuple assignment: a, b = b, a. Python evaluates the right side fully before assigning, so both values swap in a single line.

How does a Python function return multiple values?

A function returns a single tuple containing multiple values, e.g. return min(t), max(t). The caller unpacks it with tuple assignment, e.g. lo, hi = min_max(t).

What is the difference between *args gathering and scattering in Python?

Gathering happens in a function definition, e.g. def f(*args), collecting extra arguments into a tuple. Scattering happens at a function call, e.g. f(*t), spreading a tuple back out into separate arguments.

Can a tuple be used as a dictionary key in Python?

Yes. Tuples are immutable and therefore hashable, so they can be dictionary keys, e.g. directory['Cleese', 'John'] = '...'. Lists cannot be keys because they are mutable and unhashable.

Explore

Related topics

Keep going — these sit next to this concept in a real learning path.

Browse all python explainers →