[Tutor] Bitten by lexical closures
Hi. And I thought I understood python pretty well. Until I got hit by this: def f(x): ... print x cb = [lambda :f(what) for what in 1234] for c in cb:c() 4 4 4 4 And even this works what = foo for c in cb:c() foo foo foo foo I expected the output to be 1 2 3 4. Now I understand the cookbook recipe for currying: def curry(func, *args, **kwds): def callit(*moreargs, **morekwds): kw = kwds.copy() kw.update(morekwds) return func(*(args+moreargs), **kw) return callit cb = [curry(f,what) for what in 1234] gives the right functions. Regards, Igor ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Bitten by lexical closures
Are you just trying to make a continuation? On 5/3/06, Igor [EMAIL PROTECTED] wrote: Hi. And I thought I understood python pretty well. Until I got hit by this: def f(x): ... print x cb = [lambda :f(what) for what in 1234] for c in cb:c() 4 4 4 4 And even this works what = foo for c in cb:c() foo foo foo foo I expected the output to be 1 2 3 4. Now I understand the cookbook recipe for currying: def curry(func, *args, **kwds): def callit(*moreargs, **morekwds): kw = kwds.copy() kw.update(morekwds) return func(*(args+moreargs), **kw) return callit cb = [curry(f,what) for what in 1234] gives the right functions. Regards, Igor ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Bitten by lexical closures
On Wed, 2006-05-03 at 14:00 +0200, Igor wrote: Hi. And I thought I understood python pretty well. Until I got hit by this: def f(x): ... print x cb = [lambda :f(what) for what in 1234] for c in cb:c() 4 4 4 4 cb = [(lambda x=what:f(x)) for what in 1234] cb[0]() 1 A variable from the enclosing scope is normally only evaluated when used, that is when the closure is called. To capture a transient value, you need to explicitly pass it into the closure. I've gotten burned on this more than once. (snipped) Regards, Igor ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor -- Lloyd Kvam Venix Corp. 1 Court Street, Suite 378 Lebanon, NH 03766-1358 voice: 603-653-8139 fax:320-210-3409 -- Lloyd Kvam Venix Corp ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Bitten by lexical closures
Igor wrote: Hi. And I thought I understood python pretty well. Until I got hit by this: def f(x): ... print x cb = [lambda :f(what) for what in 1234] for c in cb:c() 4 4 4 4 You haven't actually created a closure because you don't have any nested scopes, you have global scope and the scope of the lambda function. A list comprehension doesn't introduce a new scope. Your list comp changes the value of the global what. Here is a simpler version of what you are doing that is not so mysterious: In [8]: what = 3 In [9]: def f(x): ...: print x ...: ...: In [10]: def anon(): : f(what) : : In [11]: anon() 3 In [12]: what = 4 In [13]: anon() 4 Here it is pretty obvious that anon() is referring to the global what, and no surprise that changing what changes the value printed by anon(). There is actually a gotcha here and maybe the code you show is a simplified version of some real code that trips over it. The problem is that the value in a closure is the value that a variable has when the scope creating the closure is exited, not the value when the closure is created. So even if your list comp is in a function (and thus a true closure) you will see the same result: In [14]: def makefuncs(): : return [lambda :f(who) for who in 1234] : In [15]: for func in makefuncs(): func() : 4 4 4 4 Each function sees the value that who had when makefuncs exited. There are two ways to work around this. One is to use a default argument to the lambda to bind the value, rather than depending on the closure. This would work for your version as well: In [16]: cb = [lambda what=what:f(what) for what in 1234] In [17]: for c in cb:c() : 1 2 3 4 Another way to do it is to make a factory function that creates a single function, and call that in the list comp. Then each value is bound in a separate closure: In [18]: def makefunc(what): : return lambda : f(what) : In [19]: cb = [makefunc(what) for what in 1234] In [20]: for c in cb:c() : 1 2 3 4 Kent ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Bitten by lexical closures
On Wed, 3 May 2006, Igor wrote: And I thought I understood python pretty well. Until I got hit by this: def f(x): ... print x cb = [lambda :f(what) for what in 1234] for c in cb:c() 4 4 4 4 Hi Igor, I think you're getting caught by something that isn't quite related to closures really, but with the way Python does for loops (or assignment, for that matter.) Take a look at the following example: ## import sys def sequence_function(seq): ... x = [None] ... results = [] ... for s in seq: ... x[0] = s ... results.append(lambda: sys.stdout.write(hello %s % x[0])) ... return results ... ## Try it out; does it behave in an expected way to you? If so, compare it against what you had earlier. The core of the problem is that Python's for loop mutates 's', so that all the newly defined functions refer to the same s. Best of wishes! ___ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor