[Tutor] Bitten by lexical closures

2006-05-03 Thread Igor
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

2006-05-03 Thread Chad Crabtree
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

2006-05-03 Thread Python
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

2006-05-03 Thread Kent Johnson
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

2006-05-03 Thread Danny Yoo


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