About lambda.

Peter Otten __peter__ at web.de
Fri Oct 22 15:15:09 CEST 2004


Xin Wang wrote:

> Some c++ guru said c++ is hard to learn but easy to
> use. Is python easy for both aspect? I found lot's of
> confused in coding.
> 
> #code
> from Tkinter import *
> 
> def on_click(m):
>     print m
> def lamb_on_click(m):
>     return lambda : on_click(m)
> root = Tk()
> btns = ['0', '1', '2', '3']
> for n in btns:
>     b = Button(root, text=n, command=lamb_on_click(n))
> #Ok!
>     b.pack()
> root.mainloop()
> #
> 
> Or,
> 
> #code
> ...
>     b = Button(root, text=n, command=lambda x=n:
> click(x)) #Ok!
> ...
> #
> 
> The code above works well too, but follow is NOT! WHY?
> 
> #code
> ...
>     b = Button(root, text=n, command=lambda :
> on_click(n))
> ...
> #
> 
> Whichever button be clicked, this only print '3'.
> 
> Keyword 'lambda' is just like a macro in c or templete
> in c++ or not?

Your problem has nothing to do with lambda.

f = lambda args: expr

is just another way to write

def f(args): return expr

You are rather fighting with scopes.

>>> funcs = []
>>> for i in range(3):
...     def f(): print i
...     funcs.append(f)
...
>>> for f in funcs: f()
...
2
2
2

Here the functions f as defined in the for loop refer to the global name
'i', which is is bound to 2 by the time f is called. 
There are various remedies, the simplest probably being

>>> funcs = []
>>> for i in range(3):
...     def f(i=i): print i
...     funcs.append(f)
...
>>> for f in funcs: f()
...
0
1
2

Here the parameter i is set to default to the value the global i is bound
to. After this is done further rebindings of the global variable don't
affect that default value. When the function is called without parameter,
it prints the default value of its parameter i instead of the global i.

Another option is to define a factory function:

>>> def make_f(i):
...     def f(): print i
...     return f
...
>>> funcs = []
>>> for i in range(3):
...     funcs.append(make_f(i))
...
>>> for f in funcs: f()
...
0
1
2

This introduces another scope every time make_f() is called to which the i
inside f() refers.

Finally you can write a class with callable instances:

>>> class F:
...     def __init__(self, i): self.i = i
...     def __call__(self): print self.i
...
>>> funcs = []
>>> for i in range(3):
...     funcs.append(F(i))
...
>>> for f in funcs: f() # f() now invokes the special method __call__()
...
0
1
2

Any state is kept in the instance. This variant would be overkill in your
example but has the benefit that you can easily modify the state, e. g. you
could increment self.i every time the instance is called and factor out
functionality in helper methods for larger problems.

Peter




More information about the Python-list mailing list