Setting variables with exec inside a function
Solution 1
You're almost there. You're trying to modify a global variable so you have to add the global
statement:
old_string = "didn't work"
new_string = "worked"
def function():
exec("global old_string; old_string = new_string")
print(old_string)
function()
If you run the following version, you'll see what happened in your version:
old_string = "didn't work"
new_string = "worked"
def function():
_locals = locals()
exec("old_string = new_string", globals(), _locals)
print(old_string)
print(_locals)
function()
output:
didn't work
{'old_string': 'worked'}
The way you ran it, you ended up trying to modify the function's local variables in exec
, which is basically undefined behavior. See the warning in the exec
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.
and the related warning on locals()
:
Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
Solution 2
As an alternative way of having exec
update your global variables from inside a function is to pass globals()
into it.
>>> def function(command):
... exec(command, globals())
...
>>> x = 1
>>> function('x += 1')
>>> print(x)
2
Unlike locals()
, updating the globals()
dictionary is expected always to update the corresponding global variable, and vice versa.
Related videos on Youtube
Spacenaut
Updated on July 09, 2022Comments
-
Spacenaut almost 2 years
I just started self teaching Python, and I need a little help with this script:
old_string = "didnt work" new_string = "worked" def function(): exec("old_string = new_string") print(old_string) function()
I want to get it so
old_string = "worked"
.-
Daniel Roseman about 10 yearsWhat's the point of this? Why do you want to use exec here?
-
atelcikti1 about 10 years"I just started self teaching Python.." sounds like they're is just trying to wrap their head around a new language...
-
-
SarcasticSully almost 7 yearsIs there a way to modify it in the scope of the calling function?
-
atelcikti1 almost 7 years@SarcasticSully there is no calling function here.. If you wonder about something tangentially associated with the question, or answer, you should ask a new question (with the proper context, code samples, etc.) Leaving new questions in comments of 3 year old answers isn't likely to be helpful for anyone...
-
Martijn Pieters over 5 yearsRather than add a
global
statement, you can simply set the namespaces:exec('old_string = new_string', globals(), globals())
. -
atelcikti1 over 5 years@MartijnPieters if you do that (
exec('..', globals(), globals())
) any variable created in the exec becomes a global -- probably not what you want? -
atelcikti1 over 5 yearsTry
function("[t for t in range(5)]")
, thenprint(t)
... Maybe too many unintended consequences? -
Martijn Pieters over 5 yearsYes, and that could be the goal. Most people forget about the ability to specify the namespaces (including new dictionaries), if you don’t want all names as globals using a custom namespace and then extracting what you need afterwards might be the better option anyway.
-
atelcikti1 over 5 years@MartijnPieters that's starting to sound like an answer ;-) While I agree with your argument in general (modulo Python leaking loop variables), I stand by my answer in this particular context for this particular user. (I've upvoted khelwood's answer since it's a useful insight).
-
atelcikti1 over 5 years@MartijnPieters Ah, I see the question has been heavily edited.. Check the history for context.
-
khelwood over 5 years@thebjorn I get
NameError: name 't' is not defined
, which is what I would expect. Are you using an old version of Python before they fixed the variables-escaping-comprehensions bug? -
atelcikti1 over 5 yearsYes 2.7 all the way! (sigh). Did they fix the for-loop variable escape too?
-
khelwood over 5 yearsNo, if I understand you correctly. I think that's intended behaviour.