1

exec() from inside a function gives a different output, even I pass all the parameter needed to that function. Consider this code:

def list_filter(expr,datalist,includelist) :
    exec(expr)
    print outlist


datalist=['try to kill me','black image']
includelist=['me']
expr="outlist = [i for i in datalist if  all(j in i for j in includelist)  ]"
exec(expr)
print outlist
list_filter(expr,datalist,includelist)

I have checked the similar case here : How does exec work with locals?

But this is a different error, where I'm using version 2.7.13 and I check under general condition, the exec() normally has no error at all. I found this problem shows up when there's a 'nested loop' inside list comprehension statement, such as using all() or any(). As in this case , if I remove the if condition from the list comprehension (make it to be expr = "outlist = [i for i in datalist ]") then I will get the correct output as usual.

Any idea why?

MSeifert
  • 145,886
  • 38
  • 333
  • 352
andio
  • 1,574
  • 9
  • 26
  • 45
  • 2
    What is the overall problem you're trying to solve? I doubt exec is the right way. – Alex Hall Aug 20 '17 at 13:22
  • There's something weird going on here because the generator expression inside of the `all(...)` is internally compiled into a sort of function call, and that seems to cause problems when combined with `exec`. It works as expected if you use a list comprehension instead: `all([j in i for j in includelist])`. That said, you probably really shouldn't be using `exec`. – Aran-Fey Aug 20 '17 at 13:58
  • @AlexHall : i want to modify inside the expr for example : **expr= "outlist= [i__strip____case__ for i in list]"** . so i can use **expr=expr.replace('__strip__','.lstrip().rstrip()' if stripmode else '')** or **expr=expr.replace('__case__','.lower()' if not casesensitive else '')**. These only 2 example of altering (strip() and lower() ) , in the real case there will be matrix style parameter happen . Rather than using normal if -then structure, i just want to try to do using string replacement method. This way i can easily modify the expression as string. – andio Aug 20 '17 at 14:54
  • Right, that definitely sounds like something that can and should be done without exec. In python functions are objects that can be used as variables just like anything else. Use that to solve your problem. If you get stuck, ask a new question without exec. – Alex Hall Aug 20 '17 at 15:02

1 Answers1

0

Almost always it's a bad idea to use exec in this case you probably shouldn't use it at all.

But since you asked: It works correctly if you pass in the variables from the local scope:

def list_filter(expr, datalist, includelist):
    exec(expr, locals())
    print outlist

I'm not very familiar with the scope rules for exec but I often found that you need to pass in the variables explicitly (especially if the exec isn't in the global scope).

In your case you could even pass them in explicitly:

def list_filter(expr, datalist, includelist):
    exec(expr, {'datalist': datalist, 'includelist': includelist})
    print outlist

It's even stated in the documentation that you may need to pass the variables from the scope to exec:

The built-in functions globals() and locals() return the current global and local dictionary, respectively, which may be useful to pass around for use by exec.

MSeifert
  • 145,886
  • 38
  • 333
  • 352