472,986 Members | 2,902 Online

# Autogenerate functions (array of lambdas)

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

Sep 6 '07 #1
5 1624
Chris Johnson <ef******@gmail.comwrites:
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.
Sep 6 '07 #2
Chris Johnson schrieb:
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
Sep 6 '07 #3
Chris Johnson <ef******@gmail.comwrote:
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)]

Sep 6 '07 #4
Chris Johnson <ef******@gmail.comwrites:
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.
Sep 6 '07 #5
On Sep 6, 3:44 am, Paul Rubin <http://phr...@NOSPAM.invalidwrote:
Chris Johnson <effig...@gmail.comwrites:
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.

Sep 6 '07 #6

This thread has been closed and replies have been disabled. Please start a new discussion.