See PyFrame_FastToLocals() in the CPython source.
Try wrapping your code in a function and notice the changes are no longer mirrored. Calls to exec() and eval() cause PyFrame_FastToLocals() to be applied afterwards, as used by the interactive console, but this doesn't happen during normal execution
Re:Exposing Python 3.6's Private Dict Version
by sabauma on Sunday May 28, 2017 @08:21PM
(#14436719)
The REPL essentially operates at the global scope, which is represented as a dictionary. Variables local to a function are not stored in a dictionary, however:
def main():
a = 1
d = locals()
print d
d['b'] = 123
print b
print d
print d is locals()
main()
Which prints {'a': 1}
Traceback (most recent call last):
File "test.py", line 23, in <module>
main()
File "test.py", line 19, in main
print b
NameError: global name 'b' is not definedThat's because the compiler doesn't know the locals() dictionary will be modified at runtime, so it treats b as a global variable (which happens to be undefined).
To show that not even changes to existing local variables work, try
def main():
a = 1
d = locals()
print d
b = None
d['b'] = 123
print b
print d
print locals()
print d is locals()
which prints {'a': 1}
None
{'a': 1, 'b': 123}
{'a': 1, 'b': None, 'd': {...}}
True
I.e. changes to the dictionary returned by locals() are ignored, and calling locals() again overrides the previous values.Why this happens has been explained, but more generally what you posted really doesn't prove anything at hand really. The dictionary created from scratch could be made an updateable view by hooking __setitem__.
If you gonna contradict with "are you sure?" for something so objectively answered by the source, it might be helpful to think if your example actually demonstrates what you're trying to prove.
Are you sure? The behavior I see suggests otherwise...