Parameterized functions of no arguments?

R

Rotwang

Hi all

Sorry if this is a dumb question, I would guess it's answered in an FAQ
somewhere but I haven't been able to find anything that helps. I'm
trying to use Tkinter to create a menu whose entries and corresponding
commands depend on some list x. I want the menu options to be labelled
by the elements of x, and upon selecting an option k I want my program
to execute a function f(k). So I tried writing something that
schematically looked like this:

def f(k):
[do something that depends on k]

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
menu.add_command(label = str(k), command = lambda: f(k))

The trouble is, whenever I open the menu and click on any entry k,
instead of evaluating f(k) it always evaluates f(x[-1]). I've also tried
this:

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f():
[do something that depends on k]
menu.add_command(label = str(k), command = f)

and it gives me the same problem. Can anybody suggest a way around this?
 
R

Rotwang

Hi all

Sorry if this is a dumb question, I would guess it's answered in an FAQ
somewhere but I haven't been able to find anything that helps. I'm
trying to use Tkinter to create a menu whose entries and corresponding
commands depend on some list x. I want the menu options to be labelled
by the elements of x, and upon selecting an option k I want my program
to execute a function f(k). So I tried writing something that
schematically looked like this:

def f(k):
[do something that depends on k]

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
menu.add_command(label = str(k), command = lambda: f(k))

The trouble is, whenever I open the menu and click on any entry k,
instead of evaluating f(k) it always evaluates f(x[-1]). I've also tried
this:

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f():
[do something that depends on k]
menu.add_command(label = str(k), command = f)


Mmmmnngh, that obviously wasn't going to work. Here's something that
does work:

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating a
set of functions indexed by an arbitrary list.
 
C

Chris Rebert

Hi all

Sorry if this is a dumb question, I would guess it's answered in an FAQ
somewhere but I haven't been able to find anything that helps. I'm trying to
use Tkinter to create a menu whose entries and corresponding commands depend
on some list x. I want the menu options to be labelled by the elements of x,
and upon selecting an option k I want my program to execute a function f(k).
So I tried writing something that schematically looked like this:

   def f(k):
       [do something that depends on k]

   menu = Tkinter.Menu(master, tearoff = 0)
   for k in x:
       menu.add_command(label = str(k), command = lambda: f(k))

The trouble is, whenever I open the menu and click on any entry k, instead
of evaluating f(k) it always evaluates f(x[-1]). I've also tried this:

   menu = Tkinter.Menu(master, tearoff = 0)
   for k in x:
       def f():
           [do something that depends on k]
       menu.add_command(label = str(k), command = f)

and it gives me the same problem. Can anybody suggest a way around this?

It's a well-known problem due to the intricacies of Python's scoping rules.
One workaround is:

for k in x:
def f(k=k):
[rest same as before]

Basically, the problem is that f() does close over the variable k, but
not the particular value of k at the time it was defined; f() looks up
the value of k anew each time it's called. But by the time you get
around to actually calling f(), the loop has terminated and thus k
retains its last value of x[-1]. Default argument values, however, are
evaluated only once and at definition-time, thus achieving the
intended behavior.

Cheers,
Chris
 
C

Carl Banks

Rotwang said:
On 11/02/2011 04:54, Rotwang wrote:
Mmmmnngh, that obviously wasn't going to work. Here's something that
does work:

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating a
set of functions indexed by an arbitrary list.

If you don't want to use keyword arguments you can define a helper
function to help create your closure:

def create_menu_command(j):
def f():
do_something_that_depends_on(j)
return f

for k in x:
menu.add_command(label=str(k),command=create_menu_command(k))


Carl Banks
 
P

Paul Rubin

Rotwang said:
menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating
a set of functions indexed by an arbitrary list.

That is a standard python idiom. These days maybe I'd use partial
evaluation:

from functools import partial

def f(k): whatever...

for k in x:
menu.add_command(label=str(k), command=partial(f, k))

the "pure" approach would be something like

def f(k): whatever...

for k in x:
menu.add_command(label=str(k),
command=(lambda x: lambda: f(x))(k))
 
R

Rotwang

Rotwang said:
Here's something that does work:

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating
a set of functions indexed by an arbitrary list.

That already seems quite elegant to me.
Thanks.


What part is inelegant to your eye?

I guess the fact that it exploits the way Python evaluates default
function arguments to achieve something other than what they were
intended for. Still, I see that Chris suggested the same solution
(thanks, Chris) so it clearly isn't something that's frowned upon.
 
R

Rotwang

Rotwang said:
On 11/02/2011 04:54, Rotwang wrote:
Mmmmnngh, that obviously wasn't going to work. Here's something that
does work:

menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating a
set of functions indexed by an arbitrary list.

If you don't want to use keyword arguments you can define a helper
function to help create your closure:

def create_menu_command(j):
def f():
do_something_that_depends_on(j)
return f

for k in x:
menu.add_command(label=str(k),command=create_menu_command(k))

Nice, thanks.
 
R

Rotwang

Rotwang said:
menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating
a set of functions indexed by an arbitrary list.

That is a standard python idiom. These days maybe I'd use partial
evaluation:

from functools import partial

def f(k): whatever...

for k in x:
menu.add_command(label=str(k), command=partial(f, k))

functools is new to me, I will look into it. Thanks.

the "pure" approach would be something like

def f(k): whatever...

for k in x:
menu.add_command(label=str(k),
command=(lambda x: lambda: f(x))(k))

I don't understand why this works. What is the difference between

(lambda x: lambda: f(x))(k)

and

lambda: f(k)

?
 
A

Arnaud Delobelle

Rotwang said:
Rotwang said:
menu = Tkinter.Menu(master, tearoff = 0)
for k in x:
def f(j = k):
[do something that depends on j]
menu.add_command(label = str(k), command = f)

Still, I'd like to know if there's a more elegant method for creating
a set of functions indexed by an arbitrary list.

That is a standard python idiom. These days maybe I'd use partial
evaluation:

from functools import partial

def f(k): whatever...

for k in x:
menu.add_command(label=str(k), command=partial(f, k))

functools is new to me, I will look into it. Thanks.

the "pure" approach would be something like

def f(k): whatever...

for k in x:
menu.add_command(label=str(k),
command=(lambda x: lambda: f(x))(k))

I don't understand why this works. What is the difference between

(lambda x: lambda: f(x))(k)

The value of k is bound to the local variable x; If k is changed later,
it doesn't affect the value of x above

Note that you can also write it:

lambda k=k: f(k)
and

lambda: f(k)

?

K not being local, If k is changed later, it does affect the above.
 
D

Dennis Lee Bieber

menu.add_command(label = str(k), command = lambda: f(k))

I'm not sure, but what effect does

menu.add_command(label=str(k), command = lambda k=k: f(k))

have on the processing.

Confusing syntax, I know... does it make more sense as
... lambda x=k: f(x)...
 
P

Paul Rubin

Dennis Lee Bieber said:
I'm not sure, but what effect does
menu.add_command(label=str(k), command = lambda k=k: f(k))
have on the processing.

In the first example, k is a free variable in the lambda, so it's
captured from the outer scope when the lambda is evaluated:
5

In the second example, k is bound as an arg to the lambda when the
lambda runs, initialized to the default value supplied when the lambda
was created:
5

In the weird looking k=k syntax, the "=k" gets k from the outer scope,
and uses it as a default value for the function arg that is bound in the
function. I.e. if k=3 then lambda k=k: ... is like lambda k=3:...
 
S

Steven D'Aprano

I don't understand why this works. What is the difference between

(lambda x: lambda: f(x))(k)

and

lambda: f(k)

?

Re-writing them as normal function defs may help. I'm going to do them in
reverse order:

# lambda: f(k)
def func():
return f(k)

When you call func() with no arguments, the body is executed and f(k) is
returned. Where do f and k come from? At runtime, Python searches the
local namespace of func(), and doesn't find either f or k. It then
searches the non-local namespace, that is, the function or method that
surrounds func (or your lambda), if any. In your case, there is no such
nested function, so finally it searches the global namespace, and finds
both f and k. But by the time the function is called, the for-loop which
sets k has reached the end, and k always has the same value.



# (lambda x: lambda: f(x))(k)
def func(x):
def inner():
return f(x)
return inner


When you call func(k), it creates a nested function. That nested function
includes a "closure", which is a copy of the namespace of func at the
time it is called. This closure includes a variable "k".

That inner function is returned and saved as the callback function. When
you call that callback function, it executes inner(), and f(k) is
returned. Where do f and k come from? As normal, Python searches the
local namespace, and doesn't find them, but then it finds the variable k
in the closure, and *not* the global k that the earlier example would
find.

This example may help:

store = []
for k in range(3):
fa = lambda: "k has the value %d" % k
fb = (lambda x: lambda: "x has the value %d" % x)(k)
print("fa inside the loop", fa())
print("fb inside the loop", fb())
store.append(fa)
store.append(fb)

for func in store:
print("func outside the loop", func())

del k

store[1]() # one of the closures
store[0]() # one of the non-closures




Hope this helps.
 
R

Rotwang

[...]

Re-writing them as normal function defs may help. I'm going to do them in
reverse order:

# lambda: f(k)
def func():
return f(k)

When you call func() with no arguments, the body is executed and f(k) is
returned. Where do f and k come from? At runtime, Python searches the
local namespace of func(), and doesn't find either f or k. It then
searches the non-local namespace, that is, the function or method that
surrounds func (or your lambda), if any. In your case, there is no such
nested function, so finally it searches the global namespace, and finds
both f and k. But by the time the function is called, the for-loop which
sets k has reached the end, and k always has the same value.



# (lambda x: lambda: f(x))(k)
def func(x):
def inner():
return f(x)
return inner


When you call func(k), it creates a nested function. That nested function
includes a "closure", which is a copy of the namespace of func at the
time it is called. This closure includes a variable "k".

That inner function is returned and saved as the callback function. When
you call that callback function, it executes inner(), and f(k) is
returned. Where do f and k come from? As normal, Python searches the
local namespace, and doesn't find them, but then it finds the variable k
in the closure, and *not* the global k that the earlier example would
find.

This example may help:

store = []
for k in range(3):
fa = lambda: "k has the value %d" % k
fb = (lambda x: lambda: "x has the value %d" % x)(k)
print("fa inside the loop", fa())
print("fb inside the loop", fb())
store.append(fa)
store.append(fb)

for func in store:
print("func outside the loop", func())

del k

store[1]() # one of the closures
store[0]() # one of the non-closures




Hope this helps.

Indeed it does. Thanks, and likewise to everyone else who replied.
 
H

Hrvoje Niksic

Chris Rebert said:
It's a well-known problem due to the intricacies of Python's scoping
rules.

Actually, it is not specific to Python's scoping rules (which mandate
local, then module-level, then built-in name lookup). The root of the
surprise is, as you correctly point out, the fact that the variable's
"cell" is shared by all iterations through the loop. Taking that into
account, it logically follows that all enclosed functions end up reading
the same value.

This is not endemic to Python, the exact same surprise is present in
Common Lisp, a language with long tradition of closures and otherwise
radically different scoping rules.

* (setq l (loop for i from 1 to 10 collect (lambda () i)))
* (mapcar #'funcall l)
(11 11 11 11 11 11 11 11 11 11)
 

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,780
Messages
2,569,611
Members
45,266
Latest member
DavidaAlla

Latest Threads

Top