469,916 Members | 1,924 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,916 developers. It's quick & easy.

Lambdas and variables

So I'm trying to generate Tkinter callback functions on the fly, but
it's not working, and I don't understand what's going on.

Here is an example program:

--------------------------
from Tkinter import *

def printSomething(x):
print x

tk = Tk()
stuff = ['foo', 'bar', 'baz', 'zif', 'zaf', 'zof']

for x in stuff:
l = Label(tk, text=x)
l.pack()
l.bind('<Enter>', lambda e: printSomething(x))

tk.mainloop()
--------------------------

The desired behaviour is that the program should print to stdout the
text on the label whenever the mouse enters that widget.

However, what actually happens is that it prints out 'zof' whenever
the mouse enters any of the widgets.

To further confuse me, if I add a bit of indirection:

--------------------------
from Tkinter import *

def makeFunction(x):
return lambda e: printSomething(x)

def printSomething(x):
print x

tk = Tk()
stuff = ['foo', 'bar', 'baz', 'zif', 'zaf', 'zof']

for x in stuff:
l = Lable(tk, text=x)
l.pack()
l.bind('<Enter>', makeFunction(x))

tk.mainloop()
--------------------------

....then everything works as it should.

I am running Python 2.3.4.

Can anyone explain this to me?

--
John.
Jul 18 '05 #1
6 1472
On 27 Jul 2004 16:35:39 -0700, John Fouhy <jf****@paradise.net.nz> wrote:
So I'm trying to generate Tkinter callback functions on the fly, but
it's not working, and I don't understand what's going on.

[snip examples]

Can anyone explain this to me?


I'll have a go.

It's to do with the environment to which a function, or a lambda, is
bound. "x" in the lambda will always refer to the x defined in the
enclosing scope, and you last left that set to "zof".

The next case with an explicit function makes it all better because
now the printSomething function has a reference to the actual element
you wish to use via it's argument.

Hope that helps!

dt.
Jul 18 '05 #2
John Fouhy wrote:
So I'm trying to generate Tkinter callback functions on the fly, but
it's not working, and I don't understand what's going on.

Here is an example program:

--------------------------
from Tkinter import *

def printSomething(x):
print x

tk = Tk()
stuff = ['foo', 'bar', 'baz', 'zif', 'zaf', 'zof']

for x in stuff:
l = Label(tk, text=x)
l.pack()
l.bind('<Enter>', lambda e: printSomething(x))

tk.mainloop()
--------------------------

The desired behaviour is that the program should print to stdout the
text on the label whenever the mouse enters that widget.

However, what actually happens is that it prints out 'zof' whenever
the mouse enters any of the widgets.

To further confuse me, if I add a bit of indirection:

--------------------------
from Tkinter import *

def makeFunction(x):
return lambda e: printSomething(x)

def printSomething(x):
print x

tk = Tk()
stuff = ['foo', 'bar', 'baz', 'zif', 'zaf', 'zof']

for x in stuff:
l = Lable(tk, text=x)
l.pack()
l.bind('<Enter>', makeFunction(x))

tk.mainloop()
--------------------------

...then everything works as it should.

I am running Python 2.3.4.

Can anyone explain this to me?


Derek has answered your question, but you might also be interested to
know that your first example would work with one small change:

for x in stuff:
l = Label(tk, text=x)
l.pack()
l.bind('<Enter>', lambda e, x=x: printSomething(x))

That is, "x" is passed as a default parameter to the lambda, which is
slightly more concise that your defining of an extra function. This
works because default parameters are evaluated once and for all at the
time of a function's definition.

James

Jul 18 '05 #3
[original post: http://groups.google.co.nz/groups?hl...ing.google.com
]

Derek Thomson <de***********@gmail.com> wrote in message news:<ma*************************************@pyth on.org>...
It's to do with the environment to which a function, or a lambda, is
bound. "x" in the lambda will always refer to the x defined in the
enclosing scope, and you last left that set to "zof".


Ok, so when I write 'lambda e: printSomething(x)', that creates a
function which, when called, will look up the value of the variable
'x', and then call printSomething on that value. And that value will
be whatever x was last set to. Is that correct?

What if I do 'lambda e: printSomething(copy.deepcopy(x))'? That
creates a function which, when called, will look up the value of 'x',
make a deep copy, and then call printSomething on it?

('cos that doesn't work either)

If what I wrote is correct, then I think I understand now. And I will
use the change James Henderson suggested. But it definitely feels
like a gotcha if I can't replace a function that just does 'return X'
with 'X' itself.

(thanks for your help, guys)

--
John.
Jul 18 '05 #4
John Fouhy wrote:
[original post: http://groups.google.co.nz/groups?hl...ing.google.com
]

Derek Thomson <de***********@gmail.com> wrote in message news:<ma*************************************@pyth on.org>...
It's to do with the environment to which a function, or a lambda, is
bound. "x" in the lambda will always refer to the x defined in the
enclosing scope, and you last left that set to "zof".
Ok, so when I write 'lambda e: printSomething(x)', that creates a
function which, when called, will look up the value of the variable
'x', and then call printSomething on that value. And that value will
be whatever x was last set to. Is that correct?


That's right, and it's an example of "Python's general late-binding
semantics", which came under so much discussion recently with respect to
generator expressions. See:

http://mail.python.org/pipermail/pyt...il/044555.html

and the surrounding threads.
What if I do 'lambda e: printSomething(copy.deepcopy(x))'? That
creates a function which, when called, will look up the value of 'x',
make a deep copy, and then call printSomething on it?

('cos that doesn't work either)
The copying won't be done till the function is called and you will still
get whatever x was last set to in the lexical scope. But I see you're
trying to associate a piece of data with a function - to create a sort
of closure - and that's exactly where the default argument trick comes
in. Default arguments can be used for this effect since they freeze the
value at the time of the function's definition.
If what I wrote is correct, then I think I understand now. And I will
use the change James Henderson suggested. But it definitely feels
like a gotcha if I can't replace a function that just does 'return X'
with 'X' itself.


I agree it's a gotcha. Interestingly you would have been forced to pass
x in as a default argument and avoided your problem until Python
introduced nested scopes in versions 2.1 and 2.2. You may find PEP 227
on statically nested scopes illuminating:

http://www.python.org/peps/pep-0227.html

James

Jul 18 '05 #5

"John Fouhy" <jf****@paradise.net.nz> wrote in message
news:c0**************************@posting.google.c om...
[original post: http://groups.google.co.nz/groups?hl...ing.google.com ]

Derek Thomson <de***********@gmail.com> wrote in message

news:<ma*************************************@pyth on.org>...
It's to do with the environment to which a function, or a lambda, is
bound. "x" in the lambda will always refer to the x defined in the
enclosing scope, and you last left that set to "zof".


Ok, so when I write 'lambda e: printSomething(x)', that creates a
function which, when called, will look up the value of the variable
'x', and then call printSomething on that value. And that value will
be whatever x was last set to. Is that correct?


To the best of my knowledge,

func = lambda <params>: <expression>

is almost exactly equivalent to and an abbreviation for

def func(<params>): return <expression>

(The main difference that I know of is in func.__name__ and the consequent
difference in repr(func). If someone knows of something more of
significance, please tell me.) So, if you understand simple functions, you
can understand lambdas -- without asking ;-).

Terry J. Reedy

Jul 18 '05 #6
James Henderson <ja***@logicalprogression.net> wrote in message news:<ma*************************************@pyth on.org>...
I agree it's a gotcha. Interestingly you would have been forced to pass
x in as a default argument and avoided your problem until Python
introduced nested scopes in versions 2.1 and 2.2. You may find PEP 227
on statically nested scopes illuminating:

http://www.python.org/peps/pep-0227.html


Heh.

""" The proposed
solution, in crude terms, implements the default argument approach
automatically. The "root=root" argument can be omitted. """

Ok, I've got it now (famous last words).

Thanks for your help :-)

--
John.
Jul 18 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by gong | last post: by
2 posts views Thread by Fernando Perez | last post: by
51 posts views Thread by bearophileHUGS | last post: by
7 posts views Thread by Michael | last post: by
5 posts views Thread by Sandman | last post: by
5 posts views Thread by Chris Johnson | last post: by
1 post views Thread by Waqarahmed | last post: by
reply views Thread by Salome Sato | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.