Eugene B
Eugene B

Reputation: 1013

List argument for a lambda function in python

I work with scipy optimize minimize package. In order to provide constraints for my problem I need to create a massive tuple. I do it in the following way:

c0 = [];
count = 0;
for i in range(0, Nx):
    for j in range(0, Ny):
        c0.append({'type': 'eq', 'fun': lambda x:  x[count]*x[count] + x[T + count]*x[T + count] + x[2*T + count]*x[2*T + count] - 1.});
        count+=1;
cons = tuple(c0);

But when the minimizer takes them to use, it always takes the terminal value of count, which obviously results into an index out of bounds error. Trying del(count) resulted into another error, so I guess there is something wrong with my understanding of the python way of lambda functions usage. Maybe there is a better, python-style way using slices and stuff? Would appreciate any help.

Upvotes: 1

Views: 2372

Answers (2)

everythingfunctional
everythingfunctional

Reputation: 871

What you've done there is create a function object which references a variable, it does not create its own copy, so that when you go to execute that function, it looks up the variable, which now has the last value from the loop.

@Holt closure is probably the safest way to a accomplish the task, albeit a bit more convoluted. I would change the name of my variable within the closure to make it clearer what is happening.

What this does is create a separate scope, so that once execution leaves the outer lambda function (having called it and returned a value which is the function object you actually want) the count variable in your function object and the count variable in your loop no longer reference the same place in memory, and you can increment the counter without affecting the variable referenced by the function.

Upvotes: 0

Holt
Holt

Reputation: 37626

The count variable in your variable is evaluated when you call the lambda function, not when you create it (unlike in some other languages). What you need to do is force a copy of this variable, one way could be to use a closure:

c0.append({
        'type': 'eq', 
        'fun': (lambda count: lambda x:  x[count]*x[count] + x[T + count]*x[T + count] + x[2*T + count]*x[2*T + count] - 1.) (count)
});

Another way (tricky way IMO) is to use as the default value for an argument:

c0.append({
    'type': 'eq', 
    'fun': lambda x, count = count:  x[count]*x[count] + x[T + count]*x[T + count] + x[2*T + count]*x[2*T + count] - 1.
});

Upvotes: 4

Related Questions