4 ms·
This is because the following is simply not a closure in Python: lambdas = [(lambda: i) for i in xrange(10)] This works, because it is a proper closure:
by lloeki 15y ago
This is because the following is simply not a closure in Python:
lambdas = [(lambda: i) for i in xrange(10)]
This works, because it is a proper closure:
def build_closure(i):
return (lambda: i)
lambdas = [build_closure(i) for i in range(10)]
print("%s" % [f() for f in lambdas])
See http://stackoverflow.com/questions/233673/lexical-closures-in-python http://stackoverflow.com/questions/233673/lexical-closures-i...
You can write something close only with lambda this way:
lambdas = [(lambda j=i: j) for i in range(10)]
The behavior is actually consistent between generators and list comprehensions, but it's giving the same result as a closure because generators are lazily evaluated (so f() is evaluated right on time, but i is still not enclosed), and work only once: running it twice will have the generator exhausted:
In [12]: lambdas_gen = ((lambda: i) for i in xrange(10))
In [13]: [f() for f in lambdas_gen]
Out[13]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [14]: [f() for f in lambdas_gen]
Out[14]: []
That's why forcing the generation with list() causes evaluation of the generator, and only then do you evaluate the f()s, hence the same result as the list comprehension case.
Again, changing it to the following properly encloses i:
lambdas_genlist = list((lambda j=i: j) for i in range(10))
- wisty 15y ago> This is because the following is simply not a closure in Python Well, they should be closures, or they shouldn't be there. This isn't a question of not understanding Python's semantic rules, it's a question of those rules being screwed. I understand why it's not consistent with generators (as you say - i isn't generated yet). I don't understand why it's not consistent with what you'd expect, namely lambdas not being closures. It's an even weirder gotcha than: def f(x = []): x.append(1) return x and we know how many people get hit with that one ;)
- lloeki 15y agoActually, you know, I lied (for the sake of simplicity). They are closures, else how would the lambda evaluate 'i'? The difference is in the binding. How closures work depend wildly on the language. With lexical closures it all comes down to how scopes are handled [0] and how and when variable binding is done [1] (notably §8). The fact that 'i' can be either bound late (giving the 'outer scope' effect) or bound early (giving the 'inner scope closure' you expect) is actually a quite useful feature (and I assure you both cases are equally useful), although admittedly a bit surprising when coming from other languages. Default argument value evaluation is a nice gotcha, but it's a trade-off I'm more than willing to accept [2]. Anyway I would definitely not qualify this as 'screwed'. [0] http://stackoverflow.com/a/292502/368409 http://stackoverflow.com/a/292502/368409 [1] http://docs.python.org/reference/executionmodel.html http://docs.python.org/reference/executionmodel.html [2] http://stackoverflow.com/a/1651284/368409 http://stackoverflow.com/a/1651284/368409