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.
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()andenumerate(), 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 parenthesesIndex 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 assignmentYou 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 tupleTuple assignment — the elegant swap
Swapping two variables traditionally needs a temporary variable:
temp = a
a = b
b = tempTuple assignment collapses this into one line:
a, b = b, aThe 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.orgReads 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) # 1Write 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) # 9Anytime 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 1Put 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) # 100zip() 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 pairsCombine 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 Falseenumerate() 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 cReach 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 checkedThis 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
| Type | Mutable? | Use when |
|---|---|---|
| str | No | Working with text; convert to a list if you need to edit characters |
| list | Yes | Adding/removing/changing elements; building a collection gradually |
| tuple | No | Returning 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 tooA 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.
Lists in Python
The mutable sequence type tuples are most often compared against.
Dictionaries in Python
Why tuples (not lists) can be used as dictionary keys.
Strings in Python
Another immutable sequence type, compared to tuples.
Data Types in Python
Where tuple fits among Python's immutable, non-primitive types.
Sets in Python
Uniqueness, fast membership checks, and set math.
Python course hub
All free Python explainers and the path into Agentic practice.