Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

When should you not use a list comprehension? PREMIUM

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
3 min. read Watch as video Python 3.9—3.13
Python Morsels
Watch as video
03:14

When should you not use a list comprehension in Python?

List comprehensions are for building up new lists

We have a list of strings that represent fruits:

>>> fruits = ["lime", "blueberry", "watermelon", "pear", "jujube"]

And we have a list comprehension that prints out each of these fruits on their own line:

>>> [print(fruit) for fruit in fruits]
lime
blueberry
watermelon
pear
jujube
[None, None, None, None, None]

While you can use list comprehension for printing, you probably shouldn't do this. While this list comprehension did print, it also gave us back a list of None values.

Our list comprehension created a list of five None values because the purpose of a list comprehension is to build a new list and that's exactly what we've done. The thing that we're appending to our new list is the return value of print (which is the default function return value of None).

list comprehensions are specifically for looping over some existing iterable and making a new list out of it.

for loops are a general purpose looping tool

A better way to print out everything in a list is to use a for loop:

>>> for fruit in fruits:
...     print(fruit)
...
lime
blueberry
watermelon
pear
jujube

A for loop is useful for looping over an iterable and performing an action.

for loops are a general purpose tool: they're for looping over an iterable to do anything. List comprehensions are a special purpose tool: they're just for looping over an iterable in order to build up a new list.

When I glance at this code, I assume that the purpose of this line is to build up a new list:

>>> [print(fruit) for fruit in fruits]

But deceptively the purpose of this code is not to build up a new list, the purpose is to print.

Don't use a list comprehension unless you care about building up a new list.

Overuse of list comprehensions

What if the list you're building is basically the same thing as the iterable you're building it from?

Take this list comprehension for example:

>>> fruit_counts = [(n, fruit) for (n, fruit) in enumerate(fruits, start=1)]

This list comprehension takes the return value of enumerate, loops over it, and unpacks it into a 2-item tuple and then repacks it into the same tuple. This list comprehension is building up a list of all the contents that enumerate gives us as we loop over it:

>>> fruit_counts
[(1, 'lime'), (2, 'blueberry'), (3, 'watermelon'), (4, 'pear'), (5, 'jujube')]

That comprehension above is essentially the same as this comprehension:

>>> fruit_counts = [x for x in enumerate(fruits, start=1)]

We're taking each of the items we get from calling enumerate and storing them in a new list.

Whenever you see a list comprehension in the format:

>>> [x for x in some_iterable]

That comprehension is looping over an iterable and turning it into a new list without changing anything at all. But there's a better way to do that in Python!

Instead of using a list comprehension we can use the list constructor:

>>> fruit_counts = list(enumerate(fruits, start=1))

The list constructor accepts an iterable and it will make a new list out of that iterable (storing each of the items it finds as it loops over that iterable).

>>> fruit_counts
[(1, 'lime'), (2, 'blueberry'), (3, 'watermelon'), (4, 'pear'), (5, 'jujube')]

Whenever your comprehension can be boiled down to x for x in something:

>>> new_list = [x for x in some_iterable]

You should probably use a list constructor instead:

>>> new_list = list(some_iterable)

The list constructor is a little bit easier to read at a glance than a list comprehension.

If I see this in some code:

fruit_counts = [(n, fruit) for (n, fruit) in enumerate(fruits, start=1)]

My brain will need to parse that line for a moment to figure out what that comprehension does.

Whereas when I see a list constructor:

fruit_counts = list(enumerate(fruits, start=1))

My brain immediately thinks, "oh we're taking the return value of enumerate and turning it into a list".

Summary

Don't use a list comprehension if you don't care about building up a new list. And even if you do care about building up a new list, there might be a shorter way to do it. For example, the list constructor is a way to take an iterable and copy that iterable into a new list (without changing anything and without filtering anything down).

Series: Comprehensions

In Python it's very common to build up new lists while looping over old lists. Partly this is because we don't mutate lists very often while looping over them.

Because we build up new lists from old ones so often, Python has a special syntax to help us with this very common operation: list comprehensions.

To track your progress on this Python Morsels topic trail, sign in or sign up.

0%
Python Morsels
Watch as video
03:14
This is a free preview of a premium screencast. You have 2 previews remaining.