3 ms·
Sure: >>> [[x*y for y in xrange(1,5)] for x in xrange(1,5)] [[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]] >>> list([x*y for y in xrange(1,5
by colanderman 8y ago
Sure:
>>> [[x*y for y in xrange(1,5)] for x in xrange(1,5)]
[[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]]
>>> list([x*y for y in xrange(1,5)] for x in xrange(1,5))
[[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]]
>>> [list(l) for l in ((x*y for y in xrange(1,5)) for x in xrange(1,5))]
[[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12], [4, 8, 12, 16]]
>>> [list(l) for l in [(x*y for y in xrange(1,5)) for x in xrange(1,5)]]
[[4, 8, 12, 16], [4, 8, 12, 16], [4, 8, 12, 16], [4, 8, 12, 16]]
The problem is that the `x` in the inner comprehension gets somehow linked to the variable `x` in the outer comprehension, rather that the value of `x`. (This matches Python's similarly bizarre closure semantics.) So, the results are "as expected" when either (a) the inner comprehension is evaluated eagerly (first and second example), or (b) the outer comprehension is evaluated lazily (second and third examples). But if both the inner and outer comprehensions are evaluated lazily, the inner comprehensions just see whatever the "last" value of `x` was, which I think to many people is surprising. (No less so than mutable default arguments, at least.)
- bakery2k 8y agoYou're right, this is the same problem that Python has with closures, for example: >>> l = [] >>> for i in range(10): ... l.append(lambda: i) ... >>> for j in range(10): ... print(l[j](), end=' ') ... 9 9 9 9 9 9 9 9 9 9 The issue isn't really closing over the variable instead of the value, but the fact that Python re-uses the same variable for each iteration of the loop. C# used to behave the same way, but its designers considered this bad enough that they made a breaking change to the language to fix it [1]. Unfortunately, C#'s solution (creating a new variable for each execution of the loop body) isn't really an option for Python. It would conflict with the rest of the language, which only uses variables scoped to whole functions. Note that Go makes the same mistake as in earlier versions of C# [2]. [1] https://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ https://ericlippert.com/2009/11/12/closing-over-the-loop-var... [2] https://play.golang.org/p/Pt6BN2Mj-WL https://play.golang.org/p/Pt6BN2Mj-WL