# GOTCHA with list comprehension

Chris Angelico rosuav at gmail.com
Wed Aug 5 11:52:03 CEST 2015

```On Wed, Aug 5, 2015 at 7:01 PM, Marko Rauhamaa <marko at pacujo.net> wrote:
> Chris Angelico <rosuav at gmail.com>:
>
>> You can chain 'for' and 'if' clauses as much as you like, and they
>> behave exactly the way you'd expect.
>
> How do you know what I'd expect?
>
> I wouldn't know what to expect myself.

A list comprehension can always be unwound into statement form.  For example:

squares = [n*n for n in range(10)]

can be unwound into:

squares = []
for n in range(10):
squares.append(n*n)

You simply take the expression at the beginning and put that into the
append() method call, and then the rest become statements. Here's a
filtered version:

odd_squares = [n*n for n in range(20) if n%2]

Which becomes:

odd_squares = []
for n in range(20):
if n%2:
odd_squares.append(n*n)

So what would you expect nested 'if' clauses to do? Well, they become
nested 'if' statements:

primes = [n for n in range(2,24) if n%2 if n%3]

primes = []
for n in range(2,24):
if n%2:
if n%3:
primes.append(n)

What if we have multiple 'for' loops? Same thing!

ways_to_get_seven = [(a,b) for a in range(1,7) for b in range(1,7) if a+b==7]

ways_to_get_seven = []
for a in range(1,7):
for b in range(1,7):
if a+b==7:
ways_to_get_seven.append((a,b))

No matter what combination of 'if' and 'for' you use, it can be
unwound like this, and it'll always behave the same way. Not sure what
to expect? Follow the simple rules of unwinding, and then read the
code that way.

Of course, if you don't know how to predict what the statement form
will do, then you won't understand what the comprehension will do. But
that's not the comprehension's fault.

peculiar = [a*x*x+b*x+c for a in range(1,10) for b in range(2,30) for
c in range(-3,5) if b*b-4*a*c>=0 if (-b+math.sqrt(b*b-4*a*c))/2/a>0
for x in (x*.01 for x in range(-500,500))]

I suppose you could graph that. Or something. But that's just bad
code, don't blame the list comp for that...

ChrisA

 To be technically correct, this unwinding should be done in a
nested function, which you then call. There are some other minor
technicalities too. But it's pretty much this.
```