Another python pearl

59 views
Skip to first unread message

Nils Bruin

unread,
Jan 17, 2011, 2:39:02 PM1/17/11
to sage-flame
Another case where Python 2.*'s leaking of iteration parameters leads
to really counterintuitive results:

This more or less does the expected thing:

>>> [ (a,b) for a in range(10) for b in range(10) if a==b ]
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8,
8), (9, 9)]

Although there's a small catch. Even though the for ... if ...
construct binds variables that are used to the left of it, and hence
in the above you might expect "a" to be the "inner variable", it is
not:

>>> [ (a,b) for a in range(3) for b in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2,
2)]

as it turns out, "if"s can be freely intermingled with "for" clauses,
so we can also execute

>>> b
2
>>> [ (a,b) for a in range(10) if a==b for b in range(10) ]
[(2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2,
8), (2, 9), (9, 0), (9, 1), (9, 2), (9, 3), (9, 4), (9, 5), (9, 6),
(9, 7), (9, 8), (9, 9)]
>>> b
9

This problem happily goes away in Python 3, where the semantics are
the same as (and this already works in Python 2.*):

>>> list( (a,b) for a in range(10) for b in range(10) if a==b )
>>> list( (a,b) for a in range(3) for b in range(3) )
>>> list( (a,b) for a in range(10) if a==b for b in range(10) )

where the last happily produces an error, even if b is globally bound
already. That is actually slightly better than the still slightly
confusing

>>> b=2
>>> list(list ( (a,b) for b in range(10)) for a in range(10) if a==b)
[[(2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2,
8), (2, 9)]]

(which is recognizable as bad programming style)
Reply all
Reply to author
Forward
0 new messages