How to create a list of functions depending on a parameter?

E

enzo michelangeli

Let's suppose I want to create a list of n functions of a single
argument, returning the sum between argument and index in the list, so
that e.g.:

f[0](10) will return 10
f[3](12) will return 15

....and so on. I had naively though of coding:

f = [lambda x: x+j for j in range(n)]

Unfortunately, each function in the list f[]() behaves as a closure,
and f[k](p) does NOT return p+k but p+j (for whatever value j has at
the moment: typically n, the last value assumed by j in the list
comprehension loop).

Is there a way of achieving my goal? (Of course, n is not a constant
known in advance, so I can't manually unroll the loop.)

Thanks for any suggestion.
 
P

Paul Rudin

enzo michelangeli said:
Let's suppose I want to create a list of n functions of a single
argument, returning the sum between argument and index in the list, so
that e.g.:

f[0](10) will return 10
f[3](12) will return 15

...and so on. I had naively though of coding:

f = [lambda x: x+j for j in range(n)]

Unfortunately, each function in the list f[]() behaves as a closure,
and f[k](p) does NOT return p+k but p+j (for whatever value j has at
the moment: typically n, the last value assumed by j in the list
comprehension loop).

Is there a way of achieving my goal? (Of course, n is not a constant
known in advance, so I can't manually unroll the loop.)

class Foo(object):

def __init__(self, pos):
self.pos = pos

def __call__(self, arg):
return self.pos + arg

f = [Foo(x) for x in range(10)]
 
D

Diez B. Roggisch

enzo said:
Let's suppose I want to create a list of n functions of a single
argument, returning the sum between argument and index in the list, so
that e.g.:

f[0](10) will return 10
f[3](12) will return 15

...and so on. I had naively though of coding:

f = [lambda x: x+j for j in range(n)]

Unfortunately, each function in the list f[]() behaves as a closure,
and f[k](p) does NOT return p+k but p+j (for whatever value j has at
the moment: typically n, the last value assumed by j in the list
comprehension loop).

Is there a way of achieving my goal? (Of course, n is not a constant
known in advance, so I can't manually unroll the loop.)

You need to capture n into the closure of the lambda:

f = [lambda x, n=n: x+j for j in xrange(n)]

Also note the use of xrange instead of range - the latter creates a
list-object while the former only returns a generator. Most of the
times, xrange is thus faster, and also most of the times less memory
consuming (exception to this rule occur for small n, and are neglible)

Diez
 
P

Paul Rudin

Diez B. Roggisch said:
enzo said:
Let's suppose I want to create a list of n functions of a single
argument, returning the sum between argument and index in the list, so
that e.g.:

f[0](10) will return 10
f[3](12) will return 15

...and so on. I had naively though of coding:

f = [lambda x: x+j for j in range(n)]

Unfortunately, each function in the list f[]() behaves as a closure,
and f[k](p) does NOT return p+k but p+j (for whatever value j has at
the moment: typically n, the last value assumed by j in the list
comprehension loop).

Is there a way of achieving my goal? (Of course, n is not a constant
known in advance, so I can't manually unroll the loop.)

You need to capture n into the closure of the lambda:

f = [lambda x, n=n: x+j for j in xrange(n)]

But be careful using such a technique with mutable arguments...
 
A

Arnaud Delobelle

Diez B. Roggisch said:
You need to capture n into the closure of the lambda:

f = [lambda x, n=n: x+j for j in xrange(n)]

You mean [lambda x, j=j: x+j for j in xrange(n)]

Another way would be [(lambda j:lambda x: x+j)(j) for j in xrange(n)]

Or more readably:

def adder(n):
return lambda x: x+n

[adder(j) for j in xrange(n)]
 
E

enzo michelangeli

Thanks guys. So far I had only come out with

f = [eval('lambda x: x+'+str(j)) for j in range(3)]

....which I hated because, as everybody knows, eval is evil :)

Enzo

Diez B. Roggisch said:
You need to capture n into the closure of the lambda:
f = [lambda x, n=n: x+j for j in xrange(n)]

You mean  [lambda x, j=j: x+j for j in xrange(n)]

Another way would be [(lambda j:lambda x: x+j)(j) for j in xrange(n)]

Or more readably:

def adder(n):
    return lambda x: x+n

[adder(j) for j in xrange(n)]
 
D

Diez B. Roggisch

Paul said:
Diez B. Roggisch said:
enzo said:
Let's suppose I want to create a list of n functions of a single
argument, returning the sum between argument and index in the list, so
that e.g.:

f[0](10) will return 10
f[3](12) will return 15

...and so on. I had naively though of coding:

f = [lambda x: x+j for j in range(n)]

Unfortunately, each function in the list f[]() behaves as a closure,
and f[k](p) does NOT return p+k but p+j (for whatever value j has at
the moment: typically n, the last value assumed by j in the list
comprehension loop).

Is there a way of achieving my goal? (Of course, n is not a constant
known in advance, so I can't manually unroll the loop.)

You need to capture n into the closure of the lambda:

f = [lambda x, n=n: x+j for j in xrange(n)]

But be careful using such a technique with mutable arguments...

What makes "this technique" more susceptible to mutable argument issues than
any other function or method-definition?

Diez
 
D

Diez B. Roggisch

Arnaud said:
Diez B. Roggisch said:
You need to capture n into the closure of the lambda:

f = [lambda x, n=n: x+j for j in xrange(n)]

You mean [lambda x, j=j: x+j for j in xrange(n)]


Ah, sorry, parentheses-problem.

Diez
 
L

Luis Zarrabeitia

class Foo(object):

def __init__(self, pos):
self.pos = pos

def __call__(self, arg):
return self.pos + arg

f = [Foo(x) for x in range(10)]

Or, without the class:

In [1]: def get_incrementor(n):
...: def inc(x):
...: return x+n
...: return inc
...:

In [3]: fs = [get_incrementor(n) for n in xrange(10)]

In [4]: fs[2](1)
Out[4]: 3
 

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,774
Messages
2,569,599
Members
45,173
Latest member
GeraldReund
Top