problem using lambdas for deferred callbacks

E

edd

I tried searching for an answer to this in the google groups archives
but I failed to come up with anything. So I hope no one minds me asking
this, though I'm sure it's been asked a million times before.

I was hoping that the following code would print "Hello, world!", one
character per line. But instead it prints an exclamation mark for each
character of the string. I'm sure it's no coincidence that the last
value of c is '!', but beyond that I don't understand what's happening.

# --- begin code ---
def callback(arg):
print arg

funcs = []
for c in 'Hello, world!':
funcs.append(lambda: callback(c))

for f in funcs:
f()
# --- end code ---

What I'm trying to do is create a 0-args callback by binding an
argument to a 1-arg function. But failing, miserably :)

I'm using Python 2.3.5 as installed by default on Mac OS X 10.4 if it
matters.

Thanks,

Edd
 
M

Marc 'BlackJack' Rintsch

I was hoping that the following code would print "Hello, world!", one
character per line. But instead it prints an exclamation mark for each
character of the string. I'm sure it's no coincidence that the last
value of c is '!', but beyond that I don't understand what's happening.

If you call the lambda function the name `c` is looked up in the
"environment". It's not local to the function.
# --- begin code ---
def callback(arg):
print arg

funcs = []
for c in 'Hello, world!':
funcs.append(lambda: callback(c))

Change this line to:

funcs.append(lambda x=c: callback(x))

Now the `c` is bound to the default argument `x` at definition time.

Ciao,
Marc 'BlackJack' Rintsch
 
F

Fredrik Lundh

I was hoping that the following code would print "Hello, world!", one
character per line. But instead it prints an exclamation mark for each
character of the string. I'm sure it's no coincidence that the last
value of c is '!', but beyond that I don't understand what's happening.

# --- begin code ---
def callback(arg):
print arg

funcs = []
for c in 'Hello, world!':
funcs.append(lambda: callback(c))

the "lambda" introduces a new function scope, and since the "c" variable
isn't defined in there, it's bound to the variable with the same name
from the outer scope. the problem here is that it's bound to the
*variable*, not the object, so *all* callbacks will use the same value.

there are several ways to bind to an object value instead; the easiest
in this case is to use a default argument value:

for c in 'Hello, world!':
funcs.append(lambda c=c: callback(c))

this turns the inner "c" variable to an argument that defaults to the
value of the outer "c" at the time the lambda was created.

</F>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top