Are List Comprehensions the Wrong Way Round?
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:
- Give someone a pen and a blank piece of paper (that’s the
list()
) - tell them to visit each room in the gallery (that’s the
for room in gallery:
) - and to look at each artwork in the room (
for artwork in room:
) - 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:
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:
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
-
The reason Python's list comprehensions are that way around is because they originate from list comprehensions in functional languages, which in turn come from the mathematical set-builder notation. See http://en.wikipedia.org/wiki/List_comprehension