Why Python Does Not Have Tuple Comprehension? (26/100 Days of Python)

Martin Mirakyan
3 min readJan 27, 2023

--

Day 26 of the “100 Days of Python” blog post series covering why Python does not support tuple comprehension

Python supports list comprehension, set comprehension, and dictionary comprehension. Yet, there is no such thing as tuple comprehension. A very natural question might arise “Why doesn’t Python have a tuple comprehension?”. In this post, we will try to go deep into understanding why that’s the case and how to create tuples using for loops in a single line.

Built-in Data Structures in Python

There are 4 built-in data structures available in Python: lists, dictionaries, sets, and tuples. Here lists, dictionaries, and sets are mutable, while tuples are immutable. This means that we can modify lists, sets, and dictionaries after creating them. Yet, tuples are immutable once they’re created.

These data structures support comprehension and one can create a new list or a dictionary in a single line with a for loop:

squares = [x**2 for x in range(10)]         # List comprehension
num2square = {x: x**2 for x in range(10)} # Dict comprehension
unique_squares = {x**2 for x in range(10)} # Set comprehension

experiment = (x**2 for x in range(10)) # This is not a tuple!

Yet, when we try to create a tuple with a for loop, we obtain a totally different result. Try to copy the code above and print the values for squares, num2square, unique_squares, and experiment. You’ll probably notice that the value of experiment is not a tuple.

What is the result of the tuple comprehension?

If we print the result of an expression like(x**2 for x in range(10)), we’ll notice that the result is actually a <generator object>. This is because Python does not create a tuple from an expression wrapped in parentheses () but instead creates a generator that yields elements one by one:

gen = (x*x for x in range(5))
print(next(gen)) # prints 0
print(next(gen)) # prints 1
print(next(gen)) # prints 4
print(next(gen)) # prints 9
print(next(gen)) # prints 16
print(next(gen)) # StopIteration

In this example, the generator expression x*x for x in range(5) creates a generator object that generates the squares of the numbers from 0 to 4. The next() function is used to retrieve the next generated value from the generator object, in this case: 0, 1, 4, 9, 16. When reaching the end of the sequence, the next() function raises a StopIteration error.

We will discuss generators and generator expressions later in this series. For now, the main nuance is that expressions like x*x for x in range(5) do not create tuples, but instead, create generators that yield elements one by one, and each of those can be accessed using the next() function.

How to perform tuple comprehension?

As we saw above, just wrapping a for loop in () parentheses does not create a tuple. So, how can we obtain a tuple in a single line with a for loop? We can either wrap the generator in a tuple() expression or perform a list comprehension and wrap the result in a tuple():


# Wrap the generator expression with a tuple
squares = tuple(x**2 for x in range(10))
print(squares)
# (0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

# Or create a list first and then wrap it with tuple
squares = tuple([x**2 for x in range(10)])
print(squares)
# (0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

Both of the approaches result in the same final tuple. This way we can make use of any list comprehension feature — like conditionally adding elements to the final tuple, or having a value depending on a value.

Ideologically, tuples are not meant for storing variable-sized data that gets created with comprehension. Instead, the main benefit of tuples is storing values that are highly related to each other, where changing one value would change the whole object. A good example can be storing someone’s location with a tuple (latitude, longitude). Here, as soon as one of the parameters change, the whole location should be updated as the location depends on both parameters.

What’s next?

--

--