Originally popularised by the functional language Haskell, list comprehensions give you a different way to code in Python that allows you to focus on the data you're transforming, rather than the functions you use.

Anything you can do with list comprehensions you can achieve using the built-in functions map and filter, but list comprehensions provide you with a simpler to use and more readable syntax. In this article I'll show you the power of list comprehensions by going through some simple examples.

When you're specifying a list in Python, you enumerate your items and surround them with square brackets like so:

>>> wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?']

When you use list comprehensions you do the same thing, except that instead of enumerating the contents, you describe the contents as a transformation of another list. Let's take an example: we wanted to take the list of words and make them lowercase. Normally we would use the following:

>>> l = []
>>> for word in wordlist:
        l.append(word.lower())
>>> l
['hello', 'world.', 'how', 'are', 'you?']

But that's too long, and it's slow, since Python has to implicitly loop whenever we use a for statement. If we were used to functional programming we could write instead:

>>> import string
>>> map(string.lower,wordlist)
['hello', 'world.', 'how', 'are', 'you?']

That's an improvement, but it's a little cryptic. By using a list comprehension instead we could write:

>>> [word.lower() for word in wordlist]
['hello', 'world.', 'how', 'are', 'you?']

The second version isn't any shorter, but it can be clearer in many circumstances. You'll have to decide for yourself which approach suits your circumstances.

List comprehensions can also be used to completely replace the built-in filter function, for example if you wanted only the words in the list that were already lowercase you could write:

>>> [word for word in wordlist if world.islower()]
['how']

map and filter are the cornerstones of functional programming, using list comprehensions you can use either or both in an intuitive way. If we wanted to combine both map and filter in one expression it is as simple as:

>>> [word.lower() for word in wordlist if not world.islower()]
['hello', 'world.', 'are', 'you?']

You can nest list comprehensions either in the first section like so:

>>> vowels = ['a','A','e','E','i','I','o','O','u','U']]
>>> [[letter for letter in word if letter not in vowels] for word in wordlist]
[['H', 'L', 'L'], ['W', 'r', 'l', 'd'], ['h', 'w'], ['R'], ['y', '?']]

Or in the last section, allowing you to chain multiple tranformations together:

>>> [a.lower() for a in [b[i] for b in x for i in range(len(b))]]
['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'h', 'o', 'w', 'a', 'r', 'e', 'y', 'o', 'u', '?']

As you can see, when you start chaining list comprehensions together things get complicated. When you have more than one list comprehension in an expression it's a good idea to break things up on to multiple lines. You don't always need to nest list comprehensions though, if all you're doing is iterating over multiple lists you can include them in the same list comprehension.

In the following example we want to generate all permutations of guests to a dinner party, we can do it with one simple list comprehension:

>>> guests = ['Chris', 'Brendan', 'Jimmy', 'Mel', 'Mike', 'Jess']
>>> [(seat1, seat2) for seat1 in guests for seat2 in guests if seat1 != seat2]
[('Chris', 'Brendan'), ('Chris', 'Jimmy'), ('Chris', 'Mel'), ('Chris', 'Mike'), ('Chris', 'Jess'), ('Brendan', 'Chris'), ('Brendan', 'Jimmy'), ('Brendan', 'Mel'), ('Brendan', 'Mike'), ('Brendan', 'Jess'), ('Jimmy', 'Chris'), ('Jimmy', 'Brendan'), ('Jimmy', 'Mel'), ('Jimmy', 'Mike'), ('Jimmy', 'Jess'), ('Mel', 'Chris'), ('Mel', 'Brendan'), ('Mel', 'Jimmy'), ('Mel', 'Mike'), ('Mel', 'Jess'), ('Mike', 'Chris'), ('Mike', 'Brendan'), ('Mike', 'Jimmy'), ('Mike', 'Mel'), ('Mike', 'Jess'), ('Jess', 'Chris'), ('Jess', 'Brendan'), ('Jess', 'Jimmy'), ('Jess', 'Mel'), ('Jess', 'Mike')]

It's not going to replace everything in your programs, but add list comprehensions to your Python toolbox and you'll soon see that they can make your programs smaller, clearer and faster than they were previously, and since they concentrate on data rather than process, they're easy to write -- even when what you need to do is quite complicated.

Do you need help with Python? Gain advice from Builder AU forums

Comments

1

theearthboundkid - 04/07/07

Cross post from reddit.

List comprehensions are wonderful, but some of these examples are 
less clear than they could be.

>>> [a.lower() for a in [b[i] for b in x for i in range(len(b))]]
['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'h', 'o', 'w', 'a', 'r', 'e', 'y', 'o', 'u', '?']

would be clearer as

>>> [char.lower() for word in mylist for char in word]

9 times out of 10, you don't need to use range or len for your list
comprehensions.

One cool trick is to use generators instead of list comprehensions.
They're almost the same, but generators use parentheses instead of
brackets. The advantage of generators is that you can pass them to
functions or loops without wasting time building up a list object that
will then be GC'd one line down. Here's something cool you can do
with a generator:

>>> any(mytest(i) for i in mylist)

This will tell you if any member of your list meets your test criteria.
Since it uses a generator instead of a list comprehension, it stops
testing as soon as it gets back an item that tests true. A list
comprehension would go all the way through the list even if it
matched on the first test. Here's another good one:

>>> mycounter = sum(1 for i in mylist if cond(i))

This will tell you the number of items in the list that match your
condition. It's the same as:

>>> mycounter = 0
>>> for i in mylist:
... if cond(i):
... mycounter = 1
...

OK, finally, here are some insane list comprehensions from the
Python cookbook:

# sum
nl = [2, 3, 6, 18]
[j for j in [0] for i in nl for j in [j i]][-1]

# product
nl = [2, 3, 6, 18]
[j for j in [1] for i in nl for j in [j * i]][-1]

# factorial
fac = 6
[j for j in [1] for i in xrange(2, fac 1) for j in [j*i]][-1]

While you should never use these in production code (too cryptic!),
they are really insanely cool.

» Report offensive content

Leave a comment

You must read and type the 6 chars within 0..9 and A..F

* indicates mandatory fields.

1

theearthboundkid - 07/04/07

Cross post from reddit.List comprehensions are wonderful, but some of these examples are less clear than they could be. >>> [a.lower() ... more

Log in


Sign up | Forgot your password?

  • Staff Microsoft shows off IE9 preview

    This week, highlights from Microsoft's MIX10 conference and more in the Roundup. Read more »

    -- posted by Staff

  • Chris Duckett IE9's H.264 vote killed Ogg

    In a split decision by the judges, the winner of the W3C/WHATWG video codec consensus is H.264, taking home the future of video playback on the internet while loser Ogg goes home with nothing but thoughts of what might have been. Read more »

    -- posted by Chris Duckett

  • Staff Google launches Apps Marketplace

    Google launches and app store, while Mozilla plans to re-write its open-source license. More of this week's news in the Roundup. Read more »

    -- posted by Staff

What's on?

  • Optus Deal

    Broadband + home phone + PlayStation®3 in a single package price!