Honest question: why would anyone make a list comprehension that's multiple levels deep? Isn't the purpose of comprehensions to provide quick-and-dirty inline for loops, where a full for loop is too verbose?
l = [f(x, y) for x in X for y in Y if x > y]
is the same as this: l = []
for x in X:
for y in Y:
if x > y:
l.append(f(x,y))
If a list comprehension doesn't fit on one line I try to highlight this to any future reader by using one line per thing that would get nested: l = [
f(x, y)
for x in X
for y in Y
if x > y
]
Needless to say, there are still list comprehensions that are complex enough that they ought to be broken out into loops. But being nested isn't enough by itself.That said, I usually won't use deeply nested comprehensions, but sometimes it actually is the clearest way to parse something (e.g. extracting a field from nested JSON.)
citiesByState = {
'WA': ['Seattle', 'Spokane', 'Olympia'],
'OR': ['Portland', 'Salem'],
'CA': ['San Francisco', 'Los Angeles', 'San Diego'],
}
cityToState = {city: state
for state, cities in citiesByState.items()
for city in cities}
I would generally avoid multiple levels in a comprehension but there are some very simple two-level cases like this that I use occasionally. I feel that the code is easy to read, at least. cityToState = Map.fromList
[ (city, state)
| (state, cities) <- Map.toList citiesByState
, city <- cities
]
cityToState = Map.fromList $ do
(state, cities) <- Map.toList citiesByState
city <- cities
pure (city, state)
For multiple “nested loops”, I generally prefer do-notation over both list comprehensions and combinators such as concatMap, unless the structure is simple enough that the combinator version is much shorter.(>>=) = flip concatMap
These days though, you can split it in generator expressions and make it run at the end inside a comprehension, and you get best of both worlds: splitting helps readability, running it all at once in a single comprehension at the end keeps it performing.
[f(a, b) for a in some_list for b in some_list]
or even [f(a, b) for a in some_list for b in some_list if a is not b] import itertools as it
[f(a, b) for a, b in it.product(some_list, some_list) if a is not b] [f(a, b) for a, b in it.permutations(some_list, r=2)]
And then you could also use itertools.starmap instead of using a comprehension
at all, if you wanted.