Reputation: 3
So I have a string which is a function like
code = """def somefn(x):
return x + x
y = somefn(z)"""
and I am trying to run this in another function like
def otherfn(codestr):
z = 2
exec(codestr)
return y
otherfn(code)
but it gives me the error:
Traceback (most recent call last): File "C:/Users/Admin/Desktop/heh.py", line 11, in otherfn(code) File "C:/Users/Admin/Desktop/heh.py", line 9, in otherfn return y NameError: name 'y' is not defined
it works fine outside the function like
z=2
exec(codestr)
print(y)
it finds y just fine but not sure why it is bugging out when it is in the function.
How can I fix this? Is it something to do with globals() and locals()? Using Python 3.6 btw.
Upvotes: 0
Views: 60
Reputation: 25829
There are several problems with your code. First of all, you have an indentation problem - the y
gets 'defined' inside the somefn()
function, after the return
so it never actually gets the chance to get onto the stack. You need to redefine your code
to :
code = """def somefn(x):
return x + x
y = somefn(z)"""
But that's just the tip of the iceberg. The greater issue is that exec()
cannot modify the local scope of a function. This is due the fact that Python doesn't use a dict
for lookup of variables in the local scope so all changes from the exec()
do not get reflected back to the stack to enable lookup. This causes a weird issue where exec()
seemingly changes the locals()
dictionary, but Python still throws a NameError
:
def otherfn(codestr):
z = 2
exec(codestr)
print(locals()["y"]) # prints 4
return y # NameError
otherfn(code)
This is an intended behavior, as explained in the issue4831, and further pontificated in the official docs:
Note: The default locals act as described for function
locals()
below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after functionexec()
returns.
But if you have to reflect the changes you can just do a post-exec local scope update:
def otherfn(codestr):
z = 2
locals_ = locals()
exec(codestr, globals(), locals_)
y = locals_["y"]
return y
otherfn(code)
Upvotes: 1