Cannot change global variables in a function through an exec() statement?
Asked Answered
N

4

29

Why can I not change global variables from inside a function, using exec()? It works fine when the assignment statement is outside of exec(). Here is an example of my problem:

>>> myvar = 'test'
>>> def myfunc():
...     global myvar
...     exec('myvar = "changed!"')
...     print(myvar)
... 
>>> myfunc()
test
>>> print(myvar)
test
Neither answered 18/1, 2010 at 1:5 Comment(0)
B
42

Per the docs, the exec statement takes two optional expressions, defaulting to globals() and locals(), and always performs changes (if any) in the locals() one.

So, just be more explicit/specific/precise...:

>>> def myfunc():
...   exec('myvar="boooh!"', globals())
... 
>>> myfunc()
>>> myvar
'boooh!'

...and you'll be able to clobber global variables to your heart's contents.

Barracuda answered 18/1, 2010 at 1:12 Comment(3)
A small clarification - the two optional parameters don't default to globals() and locals(). If they did, then they wouldn't have to be specified.Alexisaley
In Python 3, exec() is now a function and the docs have movedNewhouse
The docs warn against using locals() i tried it anyways and it didnt work, but globals did. however i couldn't use the globals with locals during assignment. yikes.Emlyn
H
6

To add to Alex's answer: although when you omit the locals/globals arguments they default to the locals and globals of the caller, this only a convenience hack; it does not mean they are inheriting the full execution context of the caller. In particular:

a. nested scope cells are not available to the execed code. So this fails:

def f():
    foo= 1
    def g():
        exec('print foo')
    g()
f()

b. global declarations do not carry over into the execed code. So by default as in your example, written variables are put in the locals dictionary. However, you could make it work by saying

exec('global myvar\nmyvar = "changed!"')

You don't really want to be doing this if you can help it. global already isn't nice and exec is pretty much a code smell in itself! You wouldn't want to combine them unless there was really no alternative.

Haygood answered 18/1, 2010 at 1:31 Comment(0)
V
4

How about this:

>>> myvar = 'test'
>>> def myfunc():
...     exec('globals()["myvar"] = "changed!"')
...     print(myvar)
... 
>>> myfunc()
changed!
>>> print(myvar)
changed!

It worked for me in Python 2.6.

EDIT: Actually Alex Martelli's explanation is much better than mine :)

Vallation answered 18/1, 2010 at 1:12 Comment(0)
S
0

Per the exec docs, along with the code string it takes two optional parameters for globals and locals. The docs explains:

if the optional parts are omitted, the code is executed in the current scope.

To mutate globals, pass just one parameter:

If only globals is provided, it ... will be used for both the global and the local variables.

An example of how this affects functions defined inside the exec code string:

exec_text = """
foo = "abc"
def func2():
  if foo:
      print("yes")
func2()
"""

def func1():
    exec(exec_text, globals()) # Without globals(), this fails with: NameError: name 'foo' is not defined
func1()
Synovia answered 9/3, 2023 at 21:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.