Re: Parameterized functions of no arguments?
Chris Rebert writes: > 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) -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On 11/02/2011 08:19, Steven D'Aprano wrote: [...] 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. -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
Rotwang writes: > On 11/02/2011 05:42, Ben Finney wrote: > > 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. I see. If that seems like an exploit, I can see why you think it's inelegant. > Still, I see that Chris suggested the same solution (thanks, Chris) so > it clearly isn't something that's frowned upon. Indeed, in this forum we go to tedious lengths to explain that this behaviour is the case in Python, so code that depends on it may even be encouraged :-) -- \ “He that loveth father or mother more than me is not worthy of | `\me: and he that loveth son or daughter more than me is not | _o__)worthy of me.” —Jesus, as quoted in Matthew 10:37 | Ben Finney -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On Fri, 11 Feb 2011 06:32:56 +, Rotwang wrote: > 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. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
Dennis Lee Bieber writes: >> 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. 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: >>> k = 3 >>> a = lambda: k >>> k = 5 >>> a() 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: >>> k = 3 >>> a = lambda k=5: k >>> a() 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:... -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
Rotwang writes: > On 11/02/2011 06:19, Paul Rubin wrote: >> Rotwang writes: >>> 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. -- Arnaud -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On 11/02/2011 06:19, Paul Rubin wrote: Rotwang writes: 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) ? -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On 11/02/2011 05:59, Carl Banks wrote: Rotwang wrote: 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. -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On 11/02/2011 05:42, Ben Finney wrote: Rotwang writes: 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. -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
Rotwang writes: > 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)) -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
Rotwang wrote: > 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 -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On Thu, Feb 10, 2011 at 8:54 PM, Rotwang wrote: > 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 -- http://blog.rebertia.com -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
Rotwang writes: > 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. What part is inelegant to your eye? -- \ “I'm not a bad guy! I work hard, and I love my kids. So why | `\ should I spend half my Sunday hearing about how I'm going to | _o__)Hell?” —Homer Simpson | Ben Finney -- http://mail.python.org/mailman/listinfo/python-list
Re: Parameterized functions of no arguments?
On 11/02/2011 04:54, Rotwang wrote: 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. -- http://mail.python.org/mailman/listinfo/python-list
Parameterized functions of no arguments?
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? -- http://mail.python.org/mailman/listinfo/python-list