exupero's blog

An advantage of Python comprehensions

I've been refactoring a lot of Python code recently, and one idiom I tend to use liberally is Python's comprehensions for building lists, sets, and dictionaries. Often, I'm even willing to iterate over a sequence more than once if it means I can pull some logic out of a loop and into a comprehension. My reason for this isn't performance, which is at best marginally better than using a loop. What I like about comprehensions is that their syntax is constrained.

Loops, being a general language construct, are easy to add code to, but it's also easy to mix concerns in an imperative loop. Any logic that has to loop over the same sequence gets thrown into the body of the loop, and frequently the original body didn't merely call a function but manipulated values in the scope directly. Without refactoring discipline, code added to the loop is likely to end up doing the same. Eventually, the body of the loop becomes a complex manipulation of state that's hard to reason about and remove bugs from.

Comprehensions, however, are difficult to add logic to. They do one specific thing. Other than nesting loops and adding conditions, about the only way to add logic to a Python comprehension is to call a function, which neatly separates the concerns of what's in the list, set, or dictionary from the actual loop that builds it.

Thus, when refactoring complex Python code, I'm willing to turn code into even relatively elaborate comprehensions, sometimes with other comprehensions inside them. The equivalent loops are not only less declarative, but more likely to invite complexity.