Re: Parameterized functions of no arguments?

2011-02-11 Thread Hrvoje Niksic
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?

2011-02-11 Thread Rotwang

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?

2011-02-11 Thread Ben Finney
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?

2011-02-11 Thread Steven D'Aprano
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?

2011-02-11 Thread Paul Rubin
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?

2011-02-10 Thread Arnaud Delobelle
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?

2011-02-10 Thread Rotwang

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?

2011-02-10 Thread Rotwang

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?

2011-02-10 Thread Rotwang

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?

2011-02-10 Thread Paul Rubin
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?

2011-02-10 Thread Carl Banks
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?

2011-02-10 Thread Chris Rebert
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?

2011-02-10 Thread Ben Finney
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?

2011-02-10 Thread Rotwang

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?

2011-02-10 Thread 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?
--
http://mail.python.org/mailman/listinfo/python-list