Python Basics #9 — Tuples

8 min read

In today’s lesson we’ll cover tuples.

A tuple is a data type very similar to the list we covered in the previous lesson. So similar, in fact, that most people understand the differences between lists and tuples but aren’t sure when to use one over the other.

Let’s look at how tuples differ from lists, and when you should reach for a tuple instead of a list.

First, how to define a tuple.

Lists are defined with square brackets, but tuples are defined with parentheses. Let’s see an example.

Define a tuple using parentheses:

Define a tuple
>>> t1 = (1, 2, 3)
>>> t1
(1, 2, 3)

Use the type function to confirm the type:

Check the type
>>> type(t1)
<class 'tuple'>

Confirmed — it’s a tuple.

You can also define a tuple without parentheses:

Define without parentheses
>>> t2 = 1, 2, 3
>>> t2
(1, 2, 3)

Check the type again:

Check the type
>>> type(t2)
<class 'tuple'>

Same result — a tuple was created.

There’s one thing to watch out for when defining tuples (unlike lists): when a tuple has only one item, you must put a trailing comma after it.

First, define a list with a single item:

Define a list
>>> l = ['one']
>>> l
['one']

The list is fine. Let’s see what happens with a tuple:

Define a single item
>>> t = ('one')
>>> t
'one'

Notice anything strange? The parentheses are gone.

Let’s check the type:

Check the type
>>> type(t)
<class 'str'>

A string was created instead of a tuple. As I mentioned, when defining a tuple with only one item you must include a trailing comma even though there’s no next item:

Add a trailing comma
>>> t = ('one',)
>>> t
('one',)

Now the parentheses are visible. Let’s confirm the type:

Check the type
>>> type(t)
<class 'tuple'>

I said tuples are very similar to lists. Let’s see how.

Tuples, like lists, are ordered sequence types. So you can use indexing and slicing on them.

Define a tuple and print the first item by indexing:

Indexing
>>> t = 1, 2, 3, 4, 5
>>> t[0]
1

Slice the first 3 items:

Slicing
>>> t[:3]
(1, 2, 3)

Tuples are also iterable objects (like lists), so you can use a for loop.

Use iter to confirm the tuple is iterable:

Check the iterator
>>> iter(t)
<tuple_iterator object at 0x000002DBC4985640>

Confirmed. Now use a for loop:

for loop output
>>> for i in t:
...     print(i)
...
1
2
3
4
5

Now let’s look at how tuples and lists differ.

Difference 1 #

Lists allow you to change the value of an item once defined; tuples do not. Think of tuples as similar to constants in C, C++, or Go.

First, define a list and change an item’s value:

Change a list item
>>> my_list = [1, 2, 3]
>>> my_list[0] = 'one'
>>> my_list
['one', 2, 3]

Now define a tuple and try to change the first item:

Tuple change error
>>> my_tuple = 1, 2, 3
>>> my_tuple[0] = 'one'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> my_tuple
(1, 2, 3)

A TypeError says the tuple object does not support item assignment.

Difference 2 #

Lists let you add or remove items. Tuples don’t — once defined, a tuple’s length in memory is fixed, so you cannot add new items or remove existing ones.

Define a list and add a new item:

Add an item
>>> my_list = [1, 2, 3]
>>> my_list.append(4)
>>> my_list
[1, 2, 3, 4]

The new item 4 was added.

Now remove the item we added:

Delete an item
>>> my_list.remove(4)
>>> my_list
[1, 2, 3]

The new item is gone.

Note: remove takes the value to remove, not an index. If you want to remove by index, use pop instead.

Now define a tuple and try to add a new item:

Tuple append error
>>> my_tuple = 1, 2, 3
>>> my_tuple.append(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'

The error says the tuple object has no append attribute. Tuples don’t have append or remove because items can’t be added or removed.

So what methods does the tuple class have? Let’s check with help:

Check help(tuple)
>>> help(tuple)
Help on class tuple in module builtins:

class tuple(object)
|  tuple(iterable=(), /)
|
|  Built-in immutable sequence.
|
|  If no argument is given, the constructor returns an empty tuple.
|  If iterable is specified the tuple is initialized from iterable's items.
|
|  If the argument is a tuple, the return value is the same object.
|
|  Built-in subclasses:
|      asyncgen_hooks
|      UnraisableHookArgs
|
|  Methods defined here:
|
|  __add__(self, value, /)
|      Return self+value.
|
|  __contains__(self, key, /)
|      Return key in self.
|
|  __eq__(self, value, /)
|      Return self==value.
|
|  __ge__(self, value, /)
|      Return self>=value.
|
|  __getattribute__(self, name, /)
|      Return getattr(self, name).
|
|  __getitem__(self, key, /)
|      Return self[key].
|
|  __getnewargs__(self, /)
|
|  __gt__(self, value, /)
|      Return self>value.
|
|  __hash__(self, /)
|      Return hash(self).
|
|  __iter__(self, /)
|      Implement iter(self).
|
|  __le__(self, value, /)
|      Return self<=value.
|
|  __len__(self, /)
|      Return len(self).
|
|  __lt__(self, value, /)
|      Return self<value.
|
|  __mul__(self, value, /)
|      Return self*value.
|
|  __ne__(self, value, /)
|      Return self!=value.
|
|  __repr__(self, /)
|      Return repr(self).
|
|  __rmul__(self, value, /)
|      Return value*self.
|
|  count(self, value, /)
|      Return number of occurrences of value.
|
|  index(self, value, start=0, stop=9223372036854775807, /)
|      Return first index of value.
|
|      Raises ValueError if the value is not present.
|
|  ----------------------------------------------------------------------
|  Class methods defined here:
|
|  __class_getitem__(...) from builtins.type
|      See PEP 585
|
|  ----------------------------------------------------------------------
|  Static methods defined here:
|
|  __new__(*args, **kwargs) from builtins.type
|      Create and return a new object.  See help(type) for accurate signature.

At the top, you’ll see the important note: tuple is a built-in immutable sequence. At the bottom, only count and index methods are defined.

Let’s use both. First, count to find how many items in a tuple have the same value:

count method
>>> t = 1, 2, 2, 2, 3
>>> t.count(2)
3

There are 3 items with value 2.

Now index to find the index of an item with value 3:

index method
>>> t.index(3)
4

The item with value 3 is at index 4.

Difference 3 #

Tuples use less memory than lists. Let’s store the same items in both and compare:

Define a list and a tuple
my_list = ['one', 1, 1.2345, True]
my_tuple = 'one', 1, 1.2345, True

Memory used by the list:

Check list memory
>>> import sys
>>> sys.getsizeof(my_list)
120

Memory used by the tuple:

Check tuple memory
>>> sys.getsizeof(my_tuple)
72

Even with just a few items the difference is significant. Imagine the difference with millions of items.

Difference 4 #

Tuples are created faster than lists. A tuple is stored as a single memory block, while a list stores object metadata and data in two separate blocks — so list creation is slower.

Let’s compare creation times:

List creation time
# Run the code below in IPython or Jupyter Notebook.
In [1]: %timeit my_list = [1, 2, 3, 4, 5]
46.9 ns ± 0.933 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Now tuple creation:

Tuple creation time
# Run the code below in IPython or Jupyter Notebook.
In [2]: %timeit my_tuple = 1, 2, 3, 4, 5
15.5 ns ± 0.65 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

Tuples are about 3x faster to create.

Difference 5 #

Tuples have slightly faster indexed access than lists.

Let’s measure:

Compare indexing speed
In [3]: %timeit my_list[0]
37.7 ns ± 1.93 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [4]: %timeit my_tuple[0]
36.3 ns ± 1.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Indexing speeds are nearly identical between lists and tuples.

Summary: when to use a tuple vs. a list #

Tuples use less memory, are created faster, and have slightly faster indexed access than lists. But you can’t change item values, add or remove items, or sort them with sorted.

So when should you use a tuple instead of a list?

  • Use a list when you’ll mutate the data: change item values, add new items, remove items, or sort them.
  • Use a tuple when the data shouldn’t change after definition (or doesn’t need to). Tuples always use less memory and perform better.

That wraps up this lesson. In the next lesson we’ll cover a very important data type: dictionaries.

X