Steven D'Aprano <st...@remove-this-cybersource.com.au> writes: > > def foo(): > > l = [] > > for i in xrange(10): > > (lambda j: l.append((lambda: i, lambda: j)))(i) > > print [(f(), g()) for f, g in l] > > Here's a slightly less condensed version that demonstrates the same > behaviour, but may be slightly easier to understand:
The output is the same but the reasons for it are different: > def spam(): > a = [] > b = [] > for i in xrange(5): > a.append(lambda: i) > b.append(lambda i=i: i) Here, you've replaced lambda-binding by a default argument value. > print [f() for f in a] > print [f() for f in b] > > In your function foo, the name "i" is bound to the objects 0, 1, 2, ... 9 > sequentially, each time around the for-loop. Inside the loop, a function > object is created and then called: > > (lambda j: l.append((lambda: i, lambda: j)))(i) > > (Aside: this depends on scoping rules that were introduced quite late in > Python's history -- prior to version 2.2, this wouldn't work at all.) Indeed. The default-argument trick above was used to simulate it. Not coincidentally, it was around version 2.2 that Python became interesting to me... > The "outer" lambda: > > lambda j: l.append(...) > > has a name in the function namespace (a "local variable") called "j". On > entry to this function, this j is bound to the same object which is bound > to i *at the time that the function is called*. That is, each of the > sequence of "outer" lambdas get a distinct j = 0, 1, 2, ... Ahh. You've invented a new concept of localness instead, which is equivalent to the usual notion of binding. > None of the function A0, A1, A2, ... have any local variable i. i.e., i is free in the An functions and in the outer lambda... > Functions B0, B1, B2, ... similarly have no local variable j. When you > call any of the Bs, Python looks in the enclosing scopes for a variable > j. However, in this case it *does* find one, in the "outer" lambda, ... but j is bound in the outer lambda. This is not new terminology: it goes back to lambda calculus: a variable x occurs free in a term T if * T is x; * T is U V where x occurs free in U or V; or * T is lambda y.U where y is not x and x occurs free in U. If x occurs free in T then it occurs bound in lambda x.T. See also SICP. > > * Python's `for' loop works by assignment. The name `i' remains bound > > to the same storage location throughout; > > This is not necessarily true. It's true for CPython, where function > locals are implemented as fixed slots in the function object; since the > slot doesn't move relative to the function, and the function doesn't move > relative to the heap, the name i is in a fixed storage location for the > life of foo. But that's not necessarily the case for all implementations > -- locals could be stored in a data structure that moves data around > (say, a red-black tree), or objects could be free to move in the heap, or > both. Don't be silly. In a tree, the `location' would be the tree node containing the corresponding key. You're working at the wrong level of abstraction. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list