pythonscopeindirection

Function does not see locals in exec with supplied locals


Consider this working code:

x=123

def printx():
     print(x)

If I execute printx() the output would be 123 as x value exists in locals() and printx sees that.

I can also get x value like this:

locals()["x"]

But I need to run my function in exec with global and local parameters supplied, so I wrote this code, which should be identical in purpose to the first snippet:

glo = dict()
loc = dict()
exec('x=123', glo, loc)
exec('def printx():\n    print(x)', glo, loc)

However, when I call printx() I get this error:

exec('printx()', glo, loc)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "<string>", line 2, in printx
NameError: name 'x' is not defined

Yet both loc["x"] and eval('x', glo, loc) return 123 as expected.

How can I make locals and globals available for my printx function? I want printx to behave the same in exec with context?


Solution

  • If a local dict is provided, "eval" runs in that local namespace. All changes go in that local namespace. You can see that by printing loc after your commands. Both x and printx are in that namespace. Calling a function establishes a new local namespace. Python doesn't nest namespaces, so all it has available is its new (empty) local namespace and the global namespace, and x is in neither of those.

    You should probably run your first two evals with the global namespace only. That way, your code will work, even if it is a horrible way to do Python programming.