Autogenerate functions (array of lambdas)

C

Chris Johnson

What I want to do is build an array of lambda functions, like so:

a = [lambda: i for i in range(10)]

(This is just a demonstrative dummy array. I don't need better ways to
achieve the above functionality.)

print [f() for f in a]

results in: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
rather than the hoped for: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Clearly, lambda is returning the object i, which is left at the last
value of range(10). The following is my solution.

t = lambda i: lambda: i
a = [t(i) for i in range(10)]

or the somewhat more terse:

a = [(lambda i: lambda: i)(i) for i in range(10)]

This gives the behavior which, intuitively, I expected from the
original syntax. So my questions are:
1) Does this make sense as what should be done here? That is, would
this be the behavior you'd want more often than not? As I said,
intuitively, I would think the lambda would treat the iterator
variable as a constant in this context.
2) Is there a better or preferred method than the one I've found?

Thanks,
Chris
 
P

Paul Rubin

Chris Johnson said:
a = [lambda: i for i in range(10)]
print [f() for f in a]
results in: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
rather than the hoped for: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

The usual idiom is

a = [lambda i=i: i for i in range(10)]

That way i is not a free variable in the lambda.
 
D

Diez B. Roggisch

Chris said:
What I want to do is build an array of lambda functions, like so:

a = [lambda: i for i in range(10)]

(This is just a demonstrative dummy array. I don't need better ways to
achieve the above functionality.)

print [f() for f in a]

results in: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
rather than the hoped for: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Clearly, lambda is returning the object i, which is left at the last
value of range(10). The following is my solution.

t = lambda i: lambda: i
a = [t(i) for i in range(10)]

or the somewhat more terse:

a = [(lambda i: lambda: i)(i) for i in range(10)]

This gives the behavior which, intuitively, I expected from the
original syntax. So my questions are:
1) Does this make sense as what should be done here? That is, would
this be the behavior you'd want more often than not? As I said,
intuitively, I would think the lambda would treat the iterator
variable as a constant in this context.
2) Is there a better or preferred method than the one I've found?

The problem you encountered relates to the fact that the lambdas close
around the names known when they were created - not the values bound to
them.

To overcome that, bind the value you pass to a new name, like this:

a = [lambda i=i: i for i in range(10)]

Diez
 
D

Duncan Booth

Chris Johnson said:
2) Is there a better or preferred method than the one I've found?

Use function default arguments to keep the current value of i at the point
where you define the function.

a = [(lambda n=i: n) for i in range(10)]
 
H

Hrvoje Niksic

Chris Johnson said:
What I want to do is build an array of lambda functions, like so:

a = [lambda: i for i in range(10)]

Use a factory function for creating the lambdas. The explicit
function call will force a new variable binding to be created each
time, and the lambda will refer to that binding rather than to the
loop variable binding, which is reused for all loop iterations. For
example:

def makefn(i):
return lambda: i
a = [makefn(i) for i in xrange(10)]
[f() for f in a]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

The alternative is to explicitly import the value into the lambda's
parameter list, as explained by others.
 
C

Chris Johnson

Chris Johnson said:
a = [lambda: i for i in range(10)]
print [f() for f in a]
results in: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
rather than the hoped for: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

The usual idiom is

a = [lambda i=i: i for i in range(10)]

That way i is not a free variable in the lambda.

Thanks. I figured there had to be a better way than what I was doing.
 

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,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top