[Tutor] "Strange" behaviour of list comprehension

Hugo Arts hugo.yoshi at gmail.com
Wed Feb 13 15:17:54 CET 2013

```On Wed, Feb 13, 2013 at 2:45 PM, Mahadevan, Anand <Mahadea at labcorp.com>wrote:

> I'm playing around with list comprehension and in IDLE typed this in.  I
> actually wanted it to return all tuples satisfying the condition where z is
> the sum of x and y. I kind of got mixed up with the syntax, hence I put a
> comma in there instead of an "if".  I'd like some assistance understanding
> what it's doing...
> Thanks!
>
> >>> somelist = [(x,y,z) for x in range(1,10) for y in range(x,10) for z in
> range(y,10), x+y==z]
> >>> somelist
> [(1, 1, [1, 2, 3, 4, 5, 6, 7, 8, 9]), (1, 1, False), (1, 2, [2, 3, 4, 5,
> 6, 7, 8, 9]), (1, 2, False), (1, 3, [3, 4, 5, 6, 7, 8, 9]), (1, 3, False),
> (1, 4, [4, 5, 6, 7, 8, 9]), (1, 4, False), (1, 5, [5, 6, 7, 8, 9]), (1, 5,
> False), (1, 6, [6, 7, 8, 9]), (1, 6, False), (1, 7, [7, 8, 9]), (1, 7,
> False), (1, 8, [8, 9]), (1, 8, False), (1, 9, [9]), (1, 9, False), (2, 2,
> [2, 3, 4, 5, 6, 7, 8, 9]), (2, 2, False), (2, 3, [3, 4, 5, 6, 7, 8, 9]),
> (2, 3, False), (2, 4, [4, 5, 6, 7, 8, 9]), (2, 4, False), (2, 5, [5, 6, 7,
> 8, 9]), (2, 5, False), (2, 6, [6, 7, 8, 9]), (2, 6, False), (2, 7, [7, 8,
> 9]), (2, 7, False), (2, 8, [8, 9]), (2, 8, False), (2, 9, [9]), (2, 9,
> False), (3, 3, [3, 4, 5, 6, 7, 8, 9]), (3, 3, False), (3, 4, [4, 5, 6, 7,
> 8, 9]), (3, 4, False), (3, 5, [5, 6, 7, 8, 9]), (3, 5, False), (3, 6, [6,
> 7, 8, 9]), (3, 6, False), (3, 7, [7, 8, 9]), (3, 7, False), (3, 8, [8, 9]),
> (3, 8, False), (3, 9, [9]), (3, 9, False), (4, 4, [4, 5, 6, 7, 8, 9]), (4,
> 4, False), (4, 5, [5, 6, 7, 8, 9]), (4
>  , 5, False), (4, 6, [6, 7, 8, 9]), (4, 6, False), (4, 7, [7, 8, 9]), (4,
> 7, False), (4, 8, [8, 9]), (4, 8, False), (4, 9, [9]), (4, 9, False), (5,
> 5, [5, 6, 7, 8, 9]), (5, 5, False), (5, 6, [6, 7, 8, 9]), (5, 6, False),
> (5, 7, [7, 8, 9]), (5, 7, False), (5, 8, [8, 9]), (5, 8, False), (5, 9,
> [9]), (5, 9, False), (6, 6, [6, 7, 8, 9]), (6, 6, False), (6, 7, [7, 8,
> 9]), (6, 7, False), (6, 8, [8, 9]), (6, 8, False), (6, 9, [9]), (6, 9,
> False), (7, 7, [7, 8, 9]), (7, 7, False), (7, 8, [8, 9]), (7, 8, False),
> (7, 9, [9]), (7, 9, False), (8, 8, [8, 9]), (8, 8, False), (8, 9, [9]), (8,
> 9, False), (9, 9, [9]), (9, 9, False)]
>

This list comprehension is basically interpreted like so:

[(x,y,z) for x in range(1,10) for y in range(x,10) for z in (range(y,10),
x+y==z)]

To be more precise, the comma at the end is part of a tuple (you can omit
parentheses of a tuple in many cases). This makes the final for loop
iterate over the tuple (range(y, 10), x + y == z). You'll notice that this
only works if the variable 'z' is already defined, since the tuple isn't
constructed in the for loop. In other words, if we just run the code in a
clean interpreter we'll get a NameError:

Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)]
on win32
>>> [(x,y,z) for x in range(1,10) for y in range(x,10) for z in
range(y,10), x+y==z]

Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
[(x,y,z) for x in range(1,10) for y in range(x,10) for z in
range(y,10), x+y==z]
NameError: name 'z' is not defined

What happened in your case is probably you were running different
variations of the list comprehension in the same interpreter session. Since
the iterating variable in a for loop still exists after the for loop exits,
we won't encounter this error if we run a slightly different list
comprehension first:

Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)]
on win32
>>> [(x,y,z) for x in range(1,10) for y in range(x,10) for z in range(y,10)
if x+y==z]
[(1, 1, 2), (1, 2, 3), (1, 3, 4), (1, 4, 5), (1, 5, 6), (1, 6, 7), (1, 7,
8), (1, 8, 9), (2, 2, 4), (2, 3, 5), (2, 4, 6), (2, 5, 7), (2, 6, 8), (2,
7, 9), (3, 3, 6), (3, 4, 7), (3, 5, 8), (3, 6, 9), (4, 4, 8), (4, 5, 9)]
>>> z
9
>>>

As you can see, z exists after we run a for loop with z in it. Its value is
still at the value it had during the last iteration. The same applies to x
and y. Looking at your output, you had values of x, y and z in your
interpreter session such that x + y == z evaluated to False. So the third
value in your tuples is cycling between range(y, 10) and False.

HTH,
Hugo
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20130213/4db44a75/attachment-0001.html>
```