1

I am getting crazy with this kind of problem:

I have a list of string representing functions (for eval), I need first to replace the variables with generic x[0], x[1],....

Some time ago I discovered that I can do this using subs(). Then I need to generate a list of functions (to define constraints in SciPy minimize). I am trying something like:

el=[budget.v.values()[0],silly.v.values()[0]] # my list of string/equations

fl=[]
for i in range(len(el)):
    def sos(v):
        vdict = dict(zip(q.v.values(),v)) 
        return eval(el[i]).subs(vdict)
    fl.append(sos) 
    del sos # this may be unnecessary

The result for fl is:

[<function sos at 0x19a26aa28>, <function sos at 0x199e3f398>]

but the two functions always give the same result (corresponding to the last 'sos' definition). How can I retain different function definitions?

FujiApple
  • 796
  • 5
  • 16
Roberto R
  • 11
  • 1
  • _"but the two functions always give the same result (corresponding to the last 'sos' definition)"_ that is because `i` is captured by name and so when `sos()` is invoked `i` will have the final value. See [here](http://docs.python-guide.org/en/latest/writing/gotchas/#late-binding-closures) – FujiApple Aug 24 '16 at 14:16

1 Answers1

2

Your comment:

but the two functions always give the same result (corresponding to the last 'sos' definition)

Is a big clue that you've likely run into this common gotcha!

Your code isn't in a runnable form so I can't verify this but it clearly has this bug. There are various ways to fix this including using functools.partial as explained in the first link.

For example (untested as your code isn't runnable as-is):

import functools

for i in range(len(el)):
    def sos(i, v):
        vdict = dict(zip(q.v.values(),v)) 
        return eval(el[i]).subs(vdict)
    fl.append(functools.partial(sos, i))

Given this you can now refactor this code to avoid redefining the function inside the loop:

def sos(i, v):
    vdict = dict(zip([2], v))
    return eval(el[i]).subs(vdict)

for i in range(len(el)):
   fl.append(functools.partial(sos, i))

To give you a complete and runnable example:

import functools

def add_x(x, v):
    return x + v

add_5 = functools.partial(add_x, 5)
print add_5(1)

Produces:

6
FujiApple
  • 796
  • 5
  • 16
  • Thank You very much for the hint! I understood that this is due to the "late binding closure". Great Help! – Roberto R Aug 26 '16 at 15:13