Are List Comprehensions the Wrong Way Round?

2006-08-13, Comments

Let’s suppose you’re the director of a large art gallery and another art gallery wants to borrow one of your Picassos for an exhibition. First, you’ll need to list your Picassos — which means someone visiting each room in the gallery, looking in all the artworks in that room and noting down the ones by Picasso.

Python provides a couple of ways to model this problem. You can use explicit loops or list comprehensions.

Explicit Loops

picassos = list()

for room in gallery:
    for artwork in room:
        if artwork.is_picasso():
            picassos.append(artwork)

Up until Python 2.0, this was the only way to code the algorithm. It reads top-down approach and probably expresses the way you (you’re the gallery director, remember?) would address the problem:

  1. Give someone a pen and a blank piece of paper (that’s the list())
  2. tell them to visit each room in the gallery (that’s the for room in gallery:)
  3. and to look at each artwork in the room (for artwork in room:)
  4. and, if that artwork is by Picasso, add it to the list (if artwork.is_picasso(): picassos.append(artwork))

By the way, I use the term artwork and not painting since Picasso was a painter, sculptor, printmaker and ceramicist.

List Comprehensions

Python 2.0 introduced list comprehensions — a neat way of building lists from lists (or indeed any other iterable). This reverses the algorithm by putting it from the point-of-view of the someone who tours the gallery and makes the list. That someone’s task focuses on the artwork: 1. Looking at an artwork 2. if it’s a Picasso, put it on the list 3. and repeat this procedure for all the artworks in the current room 4. and for all the rooms in the gallery

Let’s try coding this bottom-up algorithm:

Broken List Comprehension!
picassos = [artwork if artwork.is_picasso()
            for artwork in room
            for room in gallery]

Python doesn’t like it!

  File "<stdin>", line 1
    picassos = [artwork if artwork.is_picasso()
                         ^
SyntaxError: invalid syntax

List Comprehensions are the Wrong Way Round

After a quick squint at the Python documentation we realise we should have written:

Corrected List Comprehension
picassos = [artwork
            for room in gallery
            for artwork in room
            if artwork.is_picasso()]

One of the things I like about Python is that you don’t often need to squint at the documentation to clear up matters of syntax. Things usually work the way you expect them to. It’s pretty much the reverse of C++ where you’re always struggling to persuade the compiler you’re talking the same language.

In this case I suppose it’s easy enough to remember how to get things right (roughly: write the explicit loop you’d need for a top-down solution, shift the inner-most expression to the front, put the whole thing in square backets) but it still seems wrong to me. To me wrong it still seems but.

Feedback