Tuple Unpacking in Python

Tuple Unpacking in Python

Python tuple unpacking means pulling values apart from a tuple.

For example:

x, y, z = (1, 2, 3)

This assigns 1 to x, 2 to y, and 3 to z.

In Python, unpacking is not limited to tuples only. You can unpack a list or a string with the same syntax.

Unpacking is more commonly known as multiple assignment, as it reminds of assigning multiple variables on the same line.

In this guide, you learn about Python tuples, tuple unpacking, and how to use tuple unpacking with other types.

What Is a Tuple in Python

In Python, a tuple is one of the basic built-in types. Similar to a list, a tuple can store elements of different kinds.

The main difference between a tuple and a list in Python is that a tuple is immutable, whereas a list is mutable. This means you cannot change the tuple in any way after it has been created.

For example, here is a tuple of three integers:

nums = (1, 2, 3)

It is worthwhile to notice right from the get-go that a tuple does not necessarily need parenthesis.

For example, you can create the same tuple as above with:

nums = 1, 2, 3

To learn more about tuples in Python, feel free to check this article.

Tuple Unpacking in Python

Tuple unpacking means pulling values apart from the tuple into separate variables.

Before jumping into details about tuple unpacking, let’s take a look at accessing tuple values in the first place.

How to Access Tuple Values in Python

In Python, you can access tuples with a similar syntax you would access an element from a list:

  1. Specify the index of the element you want to access.
  2. Use the square bracket accessing operator [] with the index to get the specific element.

Here is the syntax for accessing elements from a tuple:

tuple_values[index]

For example, let’s get the first coordinate of the tuple that represents a 3D point:

coords = 1, 2, 3

x = coords[0]

print(x)

Output:

1

Tuple Unpacking

If you need to pick all the values from a tuple into variables, use tuple unpacking.

To do this, declare a comma-separated group of variable names and assign it to the tuple. To make this work, the number of variables must match the number of items in the tuple.

For example, let’s pick all the 3D coordinate values from a tuple into separate variables x, y, and z:

coords = 1, 2, 3
x, y, z = coords

This assigns the value 1 to x, 2 to y, and 3 to z.

The Number of Variables Must Equal to the Number of Elements

Make sure you always have the same number of elements on each side of the assignment operator when unpacking a tuple.

Let’s see what happens when the number of variables exceeds the number of elements in the tuple:

coords = 1, 2, 3

x, y, z, i, j, k = coords 

Output:

ValueError: not enough values to unpack (expected 6, got 3)

As you can see, the unpacking failed because there are not enough values in the tuple to be assigned to 6 variables.

Also, if the number of new variables is less than the number of elements in the tuple, unpacking fails.

For example, let’s try to pick two values from a tuple of five:

coords = 1, 2, 3, 4, 5
x, y = coords 

Output:

ValueError: too many values to unpack (expected 2)

Here the error says there are too many values to unpack. In the tuple, there are 5 values in total, whereas we only declare two variables to store them all.

Unpack Any Number of Elements with * Operator

In the previous section, you learned the left-hand side and the right-hand side of the tuple unpacking expression must have an equal number of items.

However, you can use a wildcard * to catch an arbitrary number of elements from a tuple. This is the only way you do not need to have an equal amount of items on both sides of the expression.

For example, let’s say you have a tuple of 3 coordinate values and 3 additional values that represent something else.

Let’s unpack the three first coordinate values into separate variables x, y, and z, and then store the rest of the values into a list:

coords = 1, 2, 3, 4, 5, 6

x, y, z, *rest = coords 

Here 1 is assigned to x, 2 to y, 3 to z, and the rest of the values into rest.

You can verify this by printing the values:

print(f"x: {x}")
print(f"y: {y}")
print(f"z: {z}")
print(f"rest: {rest}")

Output:

x: 1
y: 2
z: 3
rest: [4, 5, 6]

If you are wondering what the asterisk operator * does, please take a look at this article.

In short, it is a notation that represents any number of arguments. When you use this notation in a function in Python, you can pass that function to any number of arguments. When you use it with tuple unpacking, you are able to capture any number of “left-over” values.

Notice, that you do not necessarily need to capture the “rest” of the values. You can just as well capture the n middle values.

For example, let’s get the names occurring in the middle of a tuple:

coords = 0, 1, "two", "three", 4, 5

x, y, *strings, z, k = coords

print(f"x: {x}")
print(f"y: {y}")

print(f"strings: {strings}")

print(f"z: {z}")
print(f"k: {k}")

Output:

x: 0
y: 1
strings: ['two', 'three']
z: 4
k: 5

Here the * operator unpacks any items between the first and the last two elements in the tuple. In this case, there are two strings. But there could be any number of those.

Unpacking Other Iterables in Python

In Python, tuple unpacking works for other iterable types as well. Even though we call it tuple unpacking, it should be more accurately called iterable unpacking.

For example, you can use unpacking syntax to pick values from a list or a dictionary.

Let’s see some basic examples of unpacking with other common iterable types in Python:

# List multiple assignments
nums = [1, 2, 3]
x, y, z = nums
print(f"x:{x}, y:{y}, z:{z}")
# result: x:1, y:2, z:3


# String multiple assignments
string = "Hey"
l1, l2, l3 = string
print(f"letter 1:'{l1}', letter 2:'{l2}', letter 3 :'{l3}'")
# result: letter 1:'H', letter 2:'e', letter 3 :'y'


# Dictionary multiple assignments
data = {"name": "Alice", "age": 30}
(k1, v1), (k2, v2) = data.items()
print(f"key1: {k1}, value1: {v1}")
print(f"key1: {k2}, value1: {v2}")
# result:
# key1: name, value1: Alice
# key1: age, value1: 30

When Use Tuple Unpacking in Python

So far you have learned you can use unpacking to tear apart values from an iterable in Python.

But when would you actually use such a feature?

Mainly, you can use tuple unpacking to shorten your code and to make it more readable. In the following sections, you are going to learn 5 common use cases for unpacking in Python.

Declare Multiple Values on the Same Line

The most obvious application of tuple unpacking is the ability to define multiple variables on the same line.

For example, instead of doing this:

a = 1
b = 2
c = 3

You can do:

a, b, c = 1, 2, 3

This expression makes the code shorter meanwhile it preserves the readability.

For Loops

In for loops, you commonly use unpacking syntax when looping through a dictionary. Perhaps this is something you have already done without realizing it is also tuple unpacking.

For example, let’s loop through a dictionary of data:

data = {"name": "Alice", "age": 30}

for key, value in data.items():
    print(key, value)

Output:

name Alice
age 30

The only difference doing this is you are not using the assignment operator =. Instead, you use the in operator key, value in data.items(). In this context, however, it does the exact same as the assignment operator. It assigns each key, value pair in the data dictionary to variables key and value one at a time.

Notice that this for loop could also be written this way:

data = {"name": "Alice", "age": 30}

for pair in data.items():
    key, value = pair
    print(key, value)

Here the loop treats the key-value pairs as one object. Inside the loop, you then unpack the key and value info from it.

Avoid Hard-Coded Indexes

Sometimes your code may be littered with so-called hard-coded indexes, such as items[-1] or data[1].

Meanwhile, the meaning of these may be obvious to you, someone reading your code may need to spend time understanding your intentions.

Here is an example of how hard-coded indexes may look like:

print(f"The second element is data[1] and the last one is data[-1]")

Usually, you can do a better job using unpacking instead of hard-coded indexes.

To demonstrate, let’s represent a date string (YYYY-MM-DD) in a different format (MM/DD/YYYY):

date = "2020-10-1"
components = date.split("-")

print(f"Today is {components[1]}/{components[2]}/{components[0]}")

Output:

Today is 10/1/2020

This code is difficult to read due to the hard-coded indexes components[n].

To solve the problem, unpack year, month, and date to separate variables:

date = "2020-10-1"

year, month, day = date.split("-")

print(f"Today is {month}/{day}/{year}")

Output:

Today is 10/1/2020

This drastically improves the readability of that piece of code, doesn’t it?

Slice Alternative

Similar to how you may write hard-coded indexes in your Python code, you may introduce hard-code slicing. This is usually bad practice as it also makes the code tougher to read.

For instance:

finish_order = ["Alice", "Jack", "Charlie", "Bob", "Emmanuel", "John"]

medalists = finish_order[:3]
non_medalists = finish_order[3:]

print(f"Gold: {medalists[0]}, Silver: {medalists[1]}, Bronze: {medalists[2]}")
print(f"Without medals: {', '.join(non_medalists)}")

Output:

Gold: Alice, Silver: Jack, Bronze: Charlie
Without medals: Bob, Emmanuel, John

Instead of specifying a hard-coded slice like finish_order[:3], you use unpacking:

finish_order = ["Alice", "Jack", "Charlie", "Bob", "Emmanuel", "John"]

first, second, third, *rest = finish_order

print(f"Gold: {first}, Silver: {second}, Bronze: {third}")
print(f"Without medals: {', '.join(rest)}")

Output:

Gold: Alice, Silver: Jack, Bronze: Charlie
Without medals: Bob, Emmanuel, John

This way you did not even need to use slicing to get the job done.

For someone reading your code, it is clear what you want to accomplish with the following:

first, second, third, *rest = finish_order

While reading this takes a while to wrap your head around:

medalists = finish_order[:3]
non_medalists = finish_order[3:]

Deep Unpacking

You can also use unpacking to unpack an unpacked object in one go. This is what is meant by “deep” unpacking.

For example, let’s create a tuple whose second argument is also a tuple. To access the inner tuple as one tuple, use the unpacking you learned today:

info = ("starting point", (1, 2, 3))

name, point = info

print(name, point)

Output:

starting point (1, 2, 3)

Here you unpacked two objects from the info tuple:

  • The name of the point.
  • The point as a 3D tuple.

But as the 3D point is also a tuple, you may want to unpack it too. To do this you can use deep unpacking to unpack an unpacked value in one go.

For example, let’s pull the x, y, and z values from the tuple inside the tuple:

info = ("starting point", (1, 2, 3))

name, (x, y, z) = info

print(name, x, y, z)

Output:

starting point 1 2 3

Keep in mind when doing this you need parenthesis. Otherwise, the program thinks you are trying to pick 4 values from a tuple with two objects.

Conclusion

Today you learned about tuple unpacking in Python.

To recap, tuple unpacking means pulling apart all the values from a tuple into separate variables.

For example:

numbers = (1, 2 ,3)

x, y, z = numbers

In Python, tuple unpacking works for other iterable types too. Due to this, a more general term for tuple unpacking is unpacking or multiple assignment.

Common use cases of tuple unpacking in Python include:

  • Assign values on one line of code.
  • For loops on dictionaries.
  • Avoid hard-coded indexes.
  • An alternative to slicing.
  • Deep unpacking.

Thanks for reading. I hope you find it useful.

Happy coding!

Further Reading

Python Interview Questions

Share on facebook
Share on twitter
Share on linkedin

Leave a Comment

Your email address will not be published.