[lambda]Is the behavior expected?

A

Alphones

Hi all,


def getFunc(x):
return lambda y : x + y

if __name__ == '__main__':
todo = []
proc = getFunc(1)
todo.append(lambda: proc(1))
proc = getFunc(2)
todo.append(lambda: proc(1))
proc = getFunc(3)
todo.append(lambda: proc(1))

todo.append(lambda: getFunc(1)(1))
todo.append(lambda: getFunc(2)(1))
todo.append(lambda: getFunc(3)(1))

for task in todo:
print task()

-----------
the program outputs:
4
4
4
2
3
4
in Python 2.6.
is that excpected?
 
D

Diez B. Roggisch

Alphones said:
Hi all,


def getFunc(x):
return lambda y : x + y

if __name__ == '__main__':
todo = []
proc = getFunc(1)
todo.append(lambda: proc(1))
proc = getFunc(2)
todo.append(lambda: proc(1))
proc = getFunc(3)
todo.append(lambda: proc(1))

todo.append(lambda: getFunc(1)(1))
todo.append(lambda: getFunc(2)(1))
todo.append(lambda: getFunc(3)(1))

for task in todo:
print task()

-----------
the program outputs:
4
4
4
2
3
4
in Python 2.6.
is that excpected?

Yes. Creating a lambda will close over the current *names* in the scope,
*not* their respective values. For that, you need to create a new scope -
e.g. by bindig the value to a argument of the lambda:

lambdas = []
for i in xrange(10):
lambda i=i : i ** 2

for l in lambdas:
l()

Diez
 
A

Alphones

Alphones said:
def getFunc(x):
return lambda y : x + y
if __name__ == '__main__':
todo = []
proc = getFunc(1)
todo.append(lambda: proc(1))
proc = getFunc(2)
todo.append(lambda: proc(1))
proc = getFunc(3)
todo.append(lambda: proc(1))
todo.append(lambda: getFunc(1)(1))
todo.append(lambda: getFunc(2)(1))
todo.append(lambda: getFunc(3)(1))
for task in todo:
print task()
-----------
the program outputs:
4
4
4
2
3
4
in Python 2.6.
is that excpected?

Yes. Creating a lambda will close over the current *names* in the scope,
*not* their respective values. For that, you need to create a new scope -
e.g. by bindig the value to a argument of the lambda:

lambdas = []
for i in xrange(10):
lambda i=i : i ** 2

for l in lambdas:
l()

Diez

thanks :D
now I know what's wrong with my program.
it is a little deferent from other script language.
 
A

Alphones

Alphones:


See also here:http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scoping

Python doesn't have such automatic closures, probably for performance
reasons and/or maybe to keep its C implementation simpler (maybe other
people here can give you an explanation of this, I too am curious).

Bye,
bearophile

i encountered this problem when i'm writing a program for packaging
files.
the program generates tasks according to description file.
and tasks are deferent from each other, and should be excuted at the
last of program.

i have experience on Lua and have learned about scheme and haskell, so
i wrote the wrong code in python.

now my solution is, using class taskCreator to create tasks and put
them into tasklist.
class taskCreator:
def __init__(self, proc, input, output):
self.proc = proc
self.input = input
self.output = output
def call(self):
return self.proc.call(self.input, self.output)
'proc' is generated by procCreators, it's specified.

it looks more complex than lambda one.

is there any other simple way to implement it?
and does the taskCreator mean 'create a new scope'?

Suggestions will be appreciated. Thanks.

PS: i'm a newbie in Python (started from today)
 
D

Diez B. Roggisch

Alphones said:
i encountered this problem when i'm writing a program for packaging
files.
the program generates tasks according to description file.
and tasks are deferent from each other, and should be excuted at the
last of program.

i have experience on Lua and have learned about scheme and haskell, so
i wrote the wrong code in python.

now my solution is, using class taskCreator to create tasks and put
them into tasklist.
class taskCreator:
def __init__(self, proc, input, output):
self.proc = proc
self.input = input
self.output = output
def call(self):
return self.proc.call(self.input, self.output)
'proc' is generated by procCreators, it's specified.

it looks more complex than lambda one.

is there any other simple way to implement it?
and does the taskCreator mean 'create a new scope'?

Not exactly, but there is a saying:

"Closures are a poor man's objects. And objects are a poor man's closures."

What you did creating a class and instantiating was to capture the
parameters you needed at a time, and using them within call at another
time.

A similar thing happens when you create a lambda with arguments assigned, as
I showed you - as then as part of the closure of the lambda there are the
names in it's parameter list, assigned to the values the right hand sides
had at the time being.

Depending on what you want to do, both approaches - classes or closures -
are valid. The more complex a problem gets, the more explicit & powerful a
class becomes, where it might be a bit of an overhead for really simple
problems.

One thing for example classes allow - which is interesting for you - is that
it is easy to look into them, for debugging/tracing. You can take a
taskCreator (BTW, read PEP 8 about naming conventions in python) and see
what is in there, e.g. logging it before executing the task or some such.

OTOH, a lambda (or function) is an opaque thing you can't ask such things,
making life a bit harder sometimes.

Diez
 
P

Peter Otten

Alphones said:
i encountered this problem when i'm writing a program for packaging
files.
the program generates tasks according to description file.
and tasks are deferent from each other, and should be excuted at the
last of program.

i have experience on Lua and have learned about scheme and haskell, so
i wrote the wrong code in python.

now my solution is, using class taskCreator to create tasks and put
them into tasklist.
class taskCreator:
def __init__(self, proc, input, output):
self.proc = proc
self.input = input
self.output = output
def call(self):
return self.proc.call(self.input, self.output)
'proc' is generated by procCreators, it's specified.

it looks more complex than lambda one.

is there any other simple way to implement it?

Yes, you can have closures in Python (you already had one, the getFunc()
function in your initial post).


Random remarks:

You don't need an explicit call() method, you can make instances callable:.... def __init__(self, name):
.... self.name = name
.... def __call__(self):
.... print "calling", self.name
....calling foo

If your object has just this one method you can instead use a closure:
.... def call():
.... print "calling", name
.... return call
....calling bar

This can be simplified further with some help from the library:
.... print "calling", name
....calling baz

Here is a complete example:

import sys
from functools import partial

def lower(input, output):
output.writelines(line.lower() for line in input)

def prefix(input, output):
for index_line in enumerate(input):
output.write("%d: %s" % index_line)

tasks = [partial(lower, open("tmp.py"), sys.stdout),
partial(prefix, open("tmp.py"), sys.stdout)]

for t in tasks:
t()

Peter
 
M

Michele Simionato

Alphones:


See also here:http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scoping

Python doesn't have such automatic closures, probably for performance
reasons and/or maybe to keep its C implementation simpler (maybe other
people here can give you an explanation of this, I too am curious).

Python has closures which are just fine. The issue here is the for
loop.
In Python (as in Common Lisp) the for loop works by *mutating* a
single
loop index. The closures will get the latest value of the loop index.
In Haskell or Scheme the natural way to define a for loop is by
introducing a new index variable at each iteration: there is no
mutation. Each closure get its own index, with the right value.
It is a design choice: Guido and the Common Lisp implementors
are happy with mutating the loop index. I myself prefer the functional
way.

Michele Simionato
 
A

Alphones

i encountered this problem when i'm writing a program for packaging
files.
the program generates tasks according to description file.
and tasks are deferent from each other, and should be excuted at the
last of program.
i have experience on Lua and have learned about scheme and haskell, so
i wrote the wrong code in python.
now my solution is, using class taskCreator to create tasks and put
them into tasklist.
class taskCreator:
def __init__(self, proc, input, output):
self.proc = proc
self.input = input
self.output = output
def call(self):
return self.proc.call(self.input, self.output)
'proc' is generated by procCreators, it's specified.
it looks more complex than lambda one.
is there any other simple way to implement it?

Yes, you can have closures in Python (you already had one, the getFunc()
function in your initial post).

Random remarks:

You don't need an explicit call() method, you can make instances callable:>>> class A:

... def __init__(self, name):
... self.name = name
... def __call__(self):
... print "calling", self.name
...>>> a = A("foo")
calling foo

If your object has just this one method you can instead use a closure:

... def call():
... print "calling", name
... return call
...>>> a = make_a("bar")
calling bar

This can be simplified further with some help from the library:

... print "calling", name
...>>> a = partial(call, "baz")
calling baz

Here is a complete example:

import sys
from functools import partial

def lower(input, output):
output.writelines(line.lower() for line in input)

def prefix(input, output):
for index_line in enumerate(input):
output.write("%d: %s" % index_line)

tasks = [partial(lower, open("tmp.py"), sys.stdout),
partial(prefix, open("tmp.py"), sys.stdout)]

for t in tasks:
t()

Peter

functools.partial is useful to my program.
i wrote
task = partial(lambda input, output, proc: proc(input,
output), archive_input.name, archive_output, proc)
self.todo.append(task)
instead of those TaskCreators.

and wrote
def __getExportor(self, name):
if not self.exportors.has_key(name):
return lambda input, ouput: False
return partial(lambda input, output, cmdline: filter( # filter
successed exit code to make list not empty, and it means success
lambda x: x == 0 or x == 36, # hack, 36 is the success
code for kzip, i hope there isn't any program use 36 as it's failed
exit code.
map(lambda l: os.system(l), self.interpret(cmdline,
input, output))), cmdline=self.exportors[name])
instead of ProcCreators.

I know it looks more obscure and not in the 'Python Style', it's just
for me to understand these discussions :p
 
L

Lawrence D'Oliveiro

Michele said:
Guido and the Common Lisp implementors are happy with mutating the loop
index. I myself prefer the functional way.

I'd agree with you, except Python doesn't provide any way to create user-defined constants, so constant loop indices have no precedent.
 

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

Forum statistics

Threads
473,904
Messages
2,570,003
Members
46,360
Latest member
HelaineO50

Latest Threads

Top