Tuples

By the Syncfusion DevTutorials team · Last updated

Immutable ordered records: the trailing-comma trap, slicing, rebuilding instead of changing, packing and unpacking, and the two tuple methods.

Think about a printed exam certificate.

It says the name, the subject and the mark. Once it is printed, nobody changes it. If something is wrong you print a new one — you do not scratch out a number and write another.

A list is a whiteboard. A tuple is a printed certificate: the same kind of contents, fixed once written.

Here is that idea in action — a record that refuses to be changed:

A record that cannot be changed

result = ("Asha", "Maths", 78)

print(result)
print(result[0])
print(len(result))

try:
    result[2] = 90
except TypeError as error:
    print("Refused:", error)

A tuple holds many values in order, like a list, but it cannot be changed after it is made.

What You Will Learn

In this lesson, you will learn:

  • How to make a tuple, including the two cases that trip everyone up
  • Why immutability is useful rather than a limitation
  • When to choose a tuple over a list
  • How to reach items, including inside nested tuples
  • How to "change" a tuple, and what that really means
  • Packing and unpacking, which you have already used without knowing it

By the end of this lesson, you will be able to create tuples, explain why their fixed nature is useful, choose between a tuple and a list, reach items with indexes and slices, build a new tuple in place of a changed one, and pack and unpack values.

Python Tuples

Introduction to Tuples

A tuple is an ordered collection, like a list, that cannot be changed once created.

Creating Tuples

There are several ways to make a tuple, and one detail in them matters more than the rest:

Several ways to make a tuple

a = ("Asha", "Maths", 78)
b = "Asha", "Maths", 78
c = tuple(["Asha", "Maths", 78])
d = tuple("Asha")

print(a)
print(b)
print(c)
print(d)
print(a == b == c)

It is the commas that make a tuple, not the brackets. The brackets are usually there for clarity, and sometimes required to avoid ambiguity.

Empty Tuples

An empty tuple is straightforward — brackets with nothing inside:

An empty tuple

empty = ()
also_empty = tuple()

print(empty, also_empty, len(empty))
print(empty == also_empty)

Single-Element Tuples

This is the one that catches everybody.

A tuple of one needs a trailing comma

one = (78,)
not_a_tuple = (78)

print(one, type(one).__name__)
print(not_a_tuple, type(not_a_tuple).__name__)

(78) is just 78 in brackets — brackets used for grouping, as in (2 + 3) * 4. The comma is what makes it a tuple.

Where the missing comma bites

single = (78)
print("Length of (78,):", len((78,)))

try:
    print(len(single))
except TypeError as error:
    print("Refused:", error)

Tuple Characteristics

Most of a tuple's characteristics are the same as a list's, with one important exception:

CharacteristicWhat it means
Ordereditems keep the order you put them in
Indexedthe first item is at 0
Immutableit cannot be changed after it is made
Duplicates allowedthe same value can appear many times
Any typeitems need not match
Hashableit can be a dictionary key or go in a set

That last one is a real advantage a list does not have:

A tuple can be a key; a list cannot

by_position = {("Maths", 1): 78}
print(by_position[("Maths", 1)])

{["Maths", 1]: 78}

Mutable vs Immutable

A tuple refuses every kind of change:

A tuple refuses every change

marks = (78, 85)

marks[0] = 90

And it has no methods that change it

marks = (78, 85)

marks.append(60)

One subtlety worth knowing: a tuple holds references, so a mutable item inside it can still change.

A list inside a tuple can still change

record = ("Asha", [78, 85])

record[1].append(60)

print(record)

The tuple still holds the same two items — the same text and the same list. The list itself changed. The tuple is immutable; its contents are not necessarily.

A tuple freezes which objects it holds, not what those objects contain. A list inside a tuple can still grow and shrink, which is also why such a tuple cannot be a dictionary key.

That is also why a tuple containing a list cannot be a dictionary key:

A tuple is only hashable if its items are

{(78, 85): "fine"}

{("Asha", [78, 85]): "not fine"}

Lists vs Tuples

Since a tuple is so close to a list, here they are side by side:

ListTuple
Written[78, 85](78, 85)
Changeableyesno
Methodseleventwo
As a dictionary keynoyes, if its items are
Memorymoreless
Use fora collection that grows or changesa fixed record, a return value

The size difference

import sys

as_list = [78, 85, 60]
as_tuple = (78, 85, 60)

print("list: ", sys.getsizeof(as_list), "bytes")
print("tuple:", sys.getsizeof(as_tuple), "bytes")

The practical rule: a list is many of one thing; a tuple is one thing made of several parts. A list of marks is a list. One result — name, subject, mark — is a tuple.

The rule in use

results = [
    ("Asha", "Maths", 78),
    ("Ravi", "Science", 85),
]

for name, subject, mark in results:
    print(f"{name} scored {mark} in {subject}")

A list of tuples: many results, each a fixed record of three parts.

Accessing Tuple Elements

Now that you know how to make a tuple, here is how to reach what is inside one.

Tuple Indexing

Positive Indexing

Counting from the front

result = ("Asha", "Maths", 78)

print(result[0])
print(result[2])

Negative Indexing

Counting from the back

result = ("Asha", "Maths", 78)

print(result[-1])
print(result[-3])

Past the end

result = ("Asha", "Maths")
print(result[5])

Tuple Slicing

Slicing gives back a new tuple.

Slicing a tuple

marks = (78, 85, 60, 92, 45)

print(marks[1:3])
print(marks[:2])
print(marks[-2:])
print(marks[::2])
print(marks[::-1])

Slicing does not break immutability: the original is untouched and a new tuple is made.

Accessing Nested Tuples

A tuple can hold other tuples, and you reach inside with two indexes:

Tuples inside tuples

term = (("Maths", 78), ("Science", 85))

print(term[0])
print(term[0][0])
print(term[1][1])

for subject, mark in term:
    print(subject, mark)

Modifying Tuples

A tuple cannot be modified, so this section is about what to do instead — and why the restriction helps.

Tuple Immutability

There is no way to change a tuple. That is the whole point: anything holding a reference to it knows it cannot change underneath them.

Why that is useful

SCHOOL_DAYS = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")

def add_day(days):
    try:
        days.append("Saturday")
        return "changed it"
    except AttributeError:
        return "could not change it"

print(add_day(SCHOOL_DAYS))
print(SCHOOL_DAYS)

Handing a list to that function would have changed it. A tuple cannot be changed by anyone.

Creating Modified Tuples

To "change" a tuple, build a new one.

Building a changed copy

result = ("Asha", "Maths", 78)

updated = result[:2] + (90,)
print(updated)
print(result)

as_list = list(result)
as_list[2] = 90
updated = tuple(as_list)
print(updated)

The second form is the one to use when several items change: convert to a list, change it, convert back.

Concatenating Tuples

Joining tuples

term1 = (78, 85)
term2 = (60, 92)

print(term1 + term2)
print(term1 * 2)
print(term1)

Both build new tuples. += looks like a change but is not:

+= makes a new tuple

marks = (78, 85)
first = marks

marks += (60,)

print("marks:", marks)
print("first:", first)
print("Same object?", marks is first)

marks points at a new tuple. The original still exists, unchanged, and first still holds it.

Deleting Tuples

Deleting an Entire Tuple

del can remove the whole name.

Deleting the name

marks = (78, 85)

print(marks)

del marks

print(marks)

Why Individual Elements Cannot Be Deleted

One item cannot be deleted, for the same reason one cannot be changed:

Deleting one item is refused

marks = (78, 85, 60)

del marks[0]

Deleting an item would change the tuple, and a tuple cannot change. Build a new one instead:

Removing an item by rebuilding

marks = (78, 85, 60)

without_first = marks[1:]
without_85 = tuple(mark for mark in marks if mark != 85)

print(without_first)
print(without_85)
print(marks)

Tuple Operations

The remaining operations work much as they do with lists.

Concatenation and Repetition

+ and *

print((78, 85) + (60,))
print((0,) * 5)

Membership Operators

in and not in

marks = (78, 85, 60)

print(78 in marks)
print(99 in marks)
print(99 not in marks)

Iterating Through Tuples

Looping over a tuple

result = ("Asha", "Maths", 78)

for item in result:
    print(item)

for position, item in enumerate(result):
    print(position, item)

Looping is safe in a way it never is with a list: nothing can add or remove items partway through.

Tuple Packing and Unpacking

One part of tuples is worth a section of its own, because it explains things you have been using all along.

Tuple Packing

Packing is putting several values into one tuple. It happens whenever you write values separated by commas.

Packing

result = "Asha", "Maths", 78

print(result)
print(type(result).__name__)

This is why a function can appear to return several values:

A function returning 'several' values

def summarise(marks):
    return min(marks), max(marks), sum(marks)

answer = summarise([78, 85, 60])

print(answer)
print(type(answer).__name__)

It returned one tuple. Python packed the three values on the way out.

Tuple Unpacking

Unpacking is the reverse: taking a tuple apart into separate names.

Unpacking

result = ("Asha", "Maths", 78)

name, subject, mark = result

print(name)
print(subject)
print(mark)

The counts must match:

Too few names

result = ("Asha", "Maths", 78)

name, subject = result

A * collects whatever is left over:

Collecting the rest

marks = (78, 85, 60, 92)

first, *rest = marks
first, *middle, last = marks

print(first, rest)
print(first, middle, last)

The collected part is always a list, even inside a tuple unpacking.

_ is the conventional name for a value you do not want:

Ignoring part of a record

result = ("Asha", "Maths", 78)

name, _, mark = result

print(name, mark)

Multiple Variable Assignment

Packing and unpacking together explain two things you have been using since lesson 2.

Where multiple assignment comes from

a, b, c = 1, 2, 3

print(a, b, c)

The right-hand side packs into (1, 2, 3); the left-hand side unpacks it.

And why swapping works

first = 78
second = 85

first, second = second, first

print(first, second)

The right side is packed into a tuple before anything is assigned, so no temporary variable is needed and nothing is overwritten halfway.

Unpacking is what makes looping over pairs read well:

Unpacking in a loop

results = [("Asha", 78), ("Ravi", 85)]

for name, mark in results:
    print(name, mark)

for position, (name, mark) in enumerate(results, start=1):
    print(position, name, mark)

Built-in Functions with Tuples

The same built-ins that work on lists work on tuples, with one surprise:

The built-ins that work on a tuple

marks = (78, 85, 60, 92)

print("len:   ", len(marks))
print("max:   ", max(marks))
print("min:   ", min(marks))
print("sum:   ", sum(marks))
print("sorted:", sorted(marks))
print("tuple: ", tuple([78, 85]))

sorted() on a tuple gives back a list, not a tuple:

sorted gives a list

marks = (78, 20, 85)

ordered = sorted(marks)

print(ordered, type(ordered).__name__)
print(tuple(sorted(marks)))

zip builds tuples

subjects = ("Maths", "Science")
marks = (78, 85)

pairs = list(zip(subjects, marks))

print(pairs)
print(type(pairs[0]).__name__)

Every pair zip() produces is a tuple, which is why unpacking in a for loop works so naturally.

Tuple Methods

A tuple cannot change, so it has only two methods — the two that ask a question rather than change anything.

count()

count() says how many times a value appears:

count

marks = (78, 85, 78, 60, 78)

print(marks.count(78))
print(marks.count(99))

index()

index() says where a value first appears:

index

marks = (78, 85, 60, 85)

print(marks.index(85))
print(marks.index(85, 2))

index raises when the value is missing

marks = (78, 85)

print(marks.index(99))

Checking first

marks = (78, 85)

if 99 in marks:
    print(marks.index(99))
else:
    print("99 is not in the tuple")

Two methods instead of eleven is not a shortcoming. Everything a tuple leaves out is something that would change it, and not being able to change is what a tuple is for.

Common mistakes

Before moving on, here are some of the mistakes beginners make with tuples.

Each row is one mistake. The middle column is what you will actually see on screen.

MistakeWhat you seeWhy it happensFix
(78) meant as a tupleAn int, and len() failsBrackets alone do not make a tuple(78,) with a trailing comma
Assigning to an itemTypeError: 'tuple' object does not support item assignmentTuples are immutableBuild a new tuple
Calling append()AttributeError: 'tuple' object has no attribute 'append'A tuple has only count and indexUse a list, or rebuild
del marks[0]TypeError: 'tuple' object doesn't support item deletionDeleting would change itmarks[1:], or rebuild
Expecting += to change itThe old tuple is untouched+= makes a new tuple and rebinds the nameExpect a new object
Expecting a nested list to be frozenThe inner list still changesA tuple freezes its references, not its contentsKeep tuples inside tuples
A tuple with a list as a dict keyTypeError: unhashable type: 'list'All items must be hashableUse tuples all the way down
Unpacking with the wrong number of namesValueError: not enough values to unpackThe counts must matchCount both sides, or use *rest
Expecting sorted() to give a tupleA list comes backsorted() always returns a listtuple(sorted(marks))
Using a tuple for a growing collectionRebuilding on every changeA tuple is for a fixed recordUse a list

What to Remember

  • A tuple is ordered like a list but cannot be changed after it is made.
  • The comma makes a tuple, not the brackets. A tuple of one needs a trailing comma: (78,).
  • A tuple can be a dictionary key or go in a set; a list cannot.
  • A tuple freezes which objects it holds, not their contents — a list inside one can still change.
  • There is no way to change a tuple: slice and join, or convert to a list and back.
  • += on a tuple builds a new tuple and moves the name to it.
  • A tuple has only two methods, count() and index(), because everything else would change it.
  • Packing happens whenever values are separated by commas; unpacking takes them apart.
  • A function that "returns several values" returns one packed tuple.
  • a, b = b, a works because the right side is packed before anything is assigned.
  • A list is many of one thing; a tuple is one thing made of several parts.

Next: Sets