Hi, I'm Harlin and welcome to my blog. I write about Python, Alfresco and other cheesy comestibles.

Python - Comprehending List Comprehensions

List comprehensions have been in Python since 2.6.x. If you're not using list comprehensions with Python, you are likely using a for statement and iterating through an iterable like this:

trees = 'oak', 'pecan', 'hickory', 'cherry'

for tree in trees:
    print(tree)

Output:

oak
pecan
hickory
cherry

You can use this form of course to do some filtering. For example, we only print out the trees in our list that have the character 'a' somewhere in the text:

for tree in trees:
    if 'a' in tree:
        print(tree)

While it may make sense to use a long for...loop, we can use list comprehensions to shorten things up. Taking the two examples above, we can use this instead:

>>> [tree for tree in trees]
['oak', 'pecan', 'hickory', 'cherry']

We can even incorporate some filtering as well. For example, we can add the "if" statement to the end to only include trees with the 'a' character in them:

>>> [tree for tree in trees if 'a' in tree]
['oak', 'pecan']

As we add a qualifying statement to the end, we can also do something with the individual element on the left side. For example, we can include these elements and have them be uppercase:

>>> [tree.upper() for tree in trees]
['OAK', 'PECAN', 'HICKORY', 'CHERRY']

and in this example use both on left and right to make it a little more complex:

>>> [tree.title() for tree in trees if 'a' in tree or 'e' in tree]
['Oak', 'Pecan', 'Cherry']

The "if" statement on the right side can also be a compound one too if needed:

>>> [
...         tree.title() for tree in trees
...         if 'a' in tree or 'e' in tree
... ]
['Oak', 'Pecan', 'Cherry']

Keeping in step with PEP 8, there does come a point where your comprehension list gets to be too long. You can break this line up (as shown above and below) to keep things neat and readable.

List comprehensions are also great when dealing with numbers:

nums = range(0, 10)

# print out all even numbers:
print([num for num in nums if num % 2 == 0])

# print out an exponentiation of even numbers:
print([num ** 2 for num in nums if num % 2 == 0])

# Add the even numbers to a list called even_primes - 
# note that each number is converted to a string:
even_primes = []
[
    even_primes.append(
        str(num ** 2)
    ) for num in nums if num % 2 == 0
]

print(', '.join(even_primes))

As you can tell, there's a lot of things you can do with list comprehensions.

You will occasionally hear about how list comprehensions are faster than iterating through a for...loop. This is actually true but it's only faster by a tiny bit really. The loop in the brackets are using byte-compiled code while you are creating and assigning to variables during a for...loop.But, there's really not much of a difference in performance.

If you're using Python, there's a good chance that readability was a big factor in your choice to use it. If that's the case, I would advise you to use a mix of list comprehensions and for...loops where needed. Pick whevever is more readable.

So, I would say first default to a list comprehesion unless you need to make conversions to the element variable that will require more than one statement to do it.

For instance, you could do something like this:

>>> [(num, num * 2,  num ** 2) for num in nums]

...

[(0, 0, 0), (1, 2, 1), (2, 4, 4), (3, 6, 9), (4, 8, 16), (5, 10, 25), (6, 12, 36), (7, 14, 49), (8, 16, 64), (9, 18, 81)]

This is ok but if you need to do other things with each num in (num, num * 2, num ** 2), things can get a little out of hand as far as readability goes. So, default to what is most readable and you should be fine.

Any Comments, Always Welcome!