The Ultimate Guide To Python Tuples

5/5 - (7 votes)

Python has several built-in data structures such as Lists, Sets and Dictionaries (check out the articles!).
In this article, you’ll learn everything you need to know about tuples including real world examples.

A Python tuple is an immutable, ordered, and iterable container data structure that can hold arbitrary and heterogeneous immutable data elements.

Tuple Python

Tuple Motivating Example

Here’s a basic example of tuple creation and usage:

t = (1, 2, 'Python', tuple(), (42, 'hi'))

for i in range(5):
    print(t[i])

'''
1
2
Python
()
(42, 'hi')
'''

Did you ever have a function from which you wanted to return several values?

Say, you’ve got a function translate(text, target_language) that expects two arguments. The first one is the text to be translated as a string value and the second one is the target_language code as a string to which the input should be translated.

Let’s assume that the input is always in English. The output should also have two values:

  • the translated text, and
  • a confidence value showing the estimated quality of the result.

Now, how can we return both values at once?

A very common solution is to take a result object with one attribute for each value. You’d have to define this object first. Depending on the context of your program, this might be a good idea. However, in Python, there is a much simpler option – tuples!

Video

No time to read? Watch the Video!

The Ultimate Guide to Python Tuples | Intro and Theoretical Basics | Part 1/7
This is the first video of a series of videos covering the whole article. Playlist on YouTube: https://www.youtube.com/watch?v=D2i-SmUELJI&list=PLbo6ydLr984ZVxb8LemGocj44zfmsfnRY

Characteristics of Tuples

The tuple data structure is a built-in data structure of the Python language with the following characteristics:

  • Tuples are containers, you can store data in them. The Python documentation defines a container as an object which implements the method __contains__. In other words a container is something you can use the in operator on. Other examples of containers in Python are list, dict, set or frozenset. The module collection contains more container types.
  • Tuples are ordered, each element has its position or, the other way round, the position has meaning.
  • Tuples are iterable, so you can use them, for example, in a for loop.
  • Tuples are immutable which means, you can’t change a tuple once it was created. Once a tuple was created you can’t modify it anymore. Another example of an immutable data type in Python is string. You can’t modify tuples or strings in Python, instead, Python creates a new instance with the modified values. However, if a tuple contains mutable data types such as lists, the elements of those lists can change! Yet, the references in the tuple to those lists can’t.
  • Tuples are heterogenous because they can contain elements of several different data types at once. An example of a homogenous data type are strings because they can only contain characters.

Syntax

To create a tuple we put some comma-separated values in parenthesis:

t1 = (1, 2, 3, 'Python')

And we can make it even simpler, the parenthesis is not required:

t2 = 1, 2, 3, 'Python'

Both ways are perfectly valid and create a tuple containing four values, three integers and one string. Creating a tuple is also called packing. Let’s see some special tuples next:

empty_tuple = ()
one_element_tuple = 1,

The empty tuple requires parenthesis, well, because that’s all there is. If you want to create a tuple containing only one element you don’t need the parenthesis though in this case I would recommend to use parenthesis to improve readability. A single comma at the end of a line can easily be missed.

Operations

For the following examples, we assume that s and t are tuples, x, i, j, k, n are integers.

SyntaxExplanation
x in tCheck if the tuple t contains the value in variable x
x not in tCheck if the tuple t does not contain the value in variable x.
t + sConcatenate the tuples t and s. This creates a new tuple containing the values from t and s.
t * nRepeat the tuple t n times. This creates a new tuple containing n times all the values of t.
t[i]Get the element at index i
t[i:j]Get the elements from index i up to index j (excluding j) as tuple
t[i:j:k]Get the elements from index i up to index j (excluding j) taking every k-th element as tuple
len(t)Returns the length of the tuple
min(t)Returns the smallest element of the tuple
max(t)Returns the largest element of the tuple
t.count(x)Returns the number of occurrences of x in the tuple

Try it yourself:

Practical Exercise: What happens if you try to assign a value in a tuple? (e.g. t[0] = 3)

Unpacking Tuples

We already saw how to retrieve a single element from a tuple using the index. But how would you retrieve all elements from a tuple? Using indices you could do the following:

my_tuple = (1, 2, 3, 4, 5)

one = my_tuple[0]
two = my_tuple[1]
three = my_tuple[2]
four = my_tuple[3]
five = my_tuple[4]

Though the code shown above works it isn’t pythonic at all – it’s not how you do it in Python. In Python you can simply assign a tuple to several variables. So, for the example above we would write the following code:

my_tuple = (1, 2, 3, 4, 5)

one, two, three, four, five = my_tuple

And that’s all we need to get the same outcome as in the first snippet. It’s important to mention that we need as many variables on the left side as there are values in the tuple.

Sometimes we don’t need all the values from a tuple. For example, let’s say we only need the first two and the last value from our tuple. Again, we could do it using indices, yet, there is a more pythonic way. It is called extended unpacking, let me show you how it works:

my_tuple = (1, 2, 3, 4, 5)

one, *_, four, five = my_tuple

As you can see, we marked the variable called _ (underscore) with an asterisk. First, this means that ‘all the rest’ goes to this variable.

So after mapping the positions of the elements to the variables, all the remaining values go to the variable marked with the asterisk. This variable contains a list of elements after the assignment.

The other interesting thing here is the variable name _ (underscore). Though it has nothing to do especially with tuples, it is a general convention in Python to call a dummy or throw-away variable like this. Since in the example we only wanted to get the first and the two last values, call the variable containing the remaining values _. So, when another programmers read the code they understand that we don’t care about those remaining values.

When you use extended unpacking you have to be careful because it must be unambiguous. You can’t have two variables with asterisk since it would make the assignment ambiguous.

Working with Tuples

We already saw one use-case for tuples: Returning several values that belong together. If you want to process the output of such a function you need a function which accepts a tuple or you have to remove the enclosing data structure using the asterisk operator. When you pass a tuple into a function call you have to use parenthesis, if not, Python won’t understand that it’s a tuple an will interpret the values as separate arguments.

Passing Tuples to Functions

If you want to pass a tuple in a function call you have to use parenthesis.

def func(tup):
    for element in tup:
        print(element)


# call function func
func((1, 2, 3))

If we want to pass a tuple to a function where we want to use each element of the tuple as separate parameter, we have to remove the tuple using the asterisk operator *.

from math import sqrt

def distance(a, b):
    return sqrt(a**2 + b**2)


point2D = (5, 3)

# call function distance
distance(*point2D)

Try it out, what happens if you call the function distance() without using the asterisk in front of the tuple.

Named Tuples

Until now we had to know at which position in a tuple a certain value is stored. For example, if we have a tuple dot = (1.5, 98, 75, 12, 12.5), we have to know what each number stands for. If you don’t know that the element at index 0 is supposed to be the radius, at index 1 is the red value, at index 2 the green value and so on, you won’t be able to work with the tuple. And when you create a new tuple you have to be careful with the order of the values because their position gives them their meaning. Here is where namedtuples come into play. If you want to use namedtuples you have to import namedtuples from collections. The module collections is part of the Python standard library.

How to Use Namedtuples

from collections import namedtuple

Person = namedtuple('Person', 'name age country')
bob = Person('Bob', 31, 'UK')

First of all it is important to note that the imported namedtuple is a function, not a data type. More precisely namedtupleis a class factory because it’s a function that creates classes. In our example above we created a class called Person which has three elements: name, age and country. The first argument we passed to namedtupleis our class name, the second argument is a string of element names. Instead of the string we could have used a tuple or list of element names too. After creating the class Person we use it to create an instance of Person. If the vocabulary here sounds very OOP-like to you, you are on the right track. Though namedtupleare indeed tuples, under the hood Python does create a class.

With namedtuplewe don’t have to know anymore which value is at which position, instead we can simple access the values by their names, e.g.:

bob.name
bob.age
bob.country

Modifying Namedtuples

As we saw before, tuples are not mutable in Python. Since namedtuple inherit from tuple, they are immutable as well. If we want to modify a tuple we have to copy all values into a new tuple and replace the values we want to update. To do so we have several options:

Slicing

Let’s assume that it’s Bob’s birthday, so how can we update his data record?

new_values = bob[:1] + (32, ) + bob[2:]
bob = Person(*new_values)

Since our namedtuple Person contains only three values, slicing might seem a bit oversized but it shows the idea.
When we create the new namedtuple we have to remove the enclosing tuple with the asterisk operator because the constructor of Person expects three separate arguments, not a tuple. If you don’t like the asterisk operator you can also use the method _make() to create a new instance of our namedtuple.

Then, the code looks like this:

new_values = bob[:1] + (32, ) + bob[2:]
bob = Person._make(new_values)

Update values using _replace()

Another way to modify values in a namedtuple is by using the _replace() method. Yet, bare in mind, tuple are immutable, modification always means creating a new tuple. If you try the following, you get an error:

# Error!
bob.name = 'Alice'

So, one year later on his next birthday Bob moves to the U.S. How do we update our tuple using the _replace() method now? Here we go:

bob = bob._replace(age=33, country='US')

That’s it! And don’t forget the reassignment – tuples are immutable, actually we are creating a new tuple!

Extending Namedtuples

Now, we want to make our data about persons more precise. Therefore, we want to add another element called city. How can we extend our Person tuple to hold a fourth element?

new_fields = Person._fields + ('City',)
NewPerson = namedtuple('NewPerson', new_fields)

bob = NewPerson(*bob, 'Washington')

Since we don’t want to type the field names of our initial Person tuple again, we just use the attribute _fields to get a tuple of all the field names. Then, we add a tuple with the names of the fields we want to add. Next, we create a new namedtuple with an additional field and create a new instance of bob living in Washington.

Applications of Namedtuples

In some cases, named tuples can be used nicely instead of dictionaries because they are not only more lightweight than dictionary, but it is also possible to access the values with the dot-notation instead of dictionary[‘field_name’] or dictionary.get(). It is very simple to convert dictionaries to named tuples and named tuples to dictionaries.

Converting a Namedtuple to a Dictionary

This is very easy because namedtuples have a method to _asdict() which does the job for you. So, let’s see how we can convert old Bob’s data into a dictionary:

data_dict = bob._asdict()

Converting a Dictionary to Namedtuple

The conversion from dictionary to namedtuple is also very simple. All we need is the double asterisk operator ** for dictionary unpacking. This leaves us with the dictionary’s entries to be used as keyword parameters.

data = {
    'name': 'Prasanth',
    'age': 27,
    'country': 'India',
}
# Person is the same namedtuple as defined in the examples above
p = Person(**data)

Convert a List of Dictionaries to a List of Namedtuples

It is common to get data from a database as a list of dictionaries. Each dictionary contains key-value pairs where the keys are the column names from the database. Suppose that our database query returned this result:

query_results = [
    {'name': 'Alice', 'age': 25, 'country': 'Spain'},
    {'name': 'Bob', 'age': 33, 'country': 'US'},
    {'name': 'Chloe', 'country': 'France'},
    {'name': 'Dagobert', 'age': 50},
]

As you can see, some values were null in the database. Thus, some tuples contain fewer entries than others. Nevertheless, a pretty short function is enough to achieve the desired output:

def create_tuples(results):
    keys = {key for data in results for key in data.keys()}
    Person = namedtuple('Person', sorted(keys))
    Person.__new__.__defaults__ = (None, ) * len(Person._fields)
    return [Person(**data) for data in results]

# call the function
create_tuples(query_results)

Now, we can very nicely work with the data without any problems with non-existing entries and much less code than we would have needed if we had taken the list of dictionaries.

Summary

Tuples are a built-in data structure of the Python language. They are immutable, so you cant modify the values inside. Instead, a new tuple has to be created.

Tuples are containers for data that belong together, such as the x and y coordinates of a point in the 2D space.

Namedtuples are also tuples but they have a decisive advantage: You can access the elements in the tuple by name, not only by index. This makes it possible to convert easily between namedtuples and dictionaries.

Any advanced Python developer has to know tuples since they are ubiquitous in Python. After working through this article, you should have no problems solving the following code puzzle:

x, *_, y = 1, 2, 3, 4
x, y = y, x

# What's the output?
print(y)

To test if you guessed correctly, use the following interactive shell to test your guess: