5 ms·
Back in Python 1.x, function locals used a dictionary, just like module globals. This made it possible do to: def f(): from math import * return cos(
by eesmith 2y ago
Back in Python 1.x, function locals used a dictionary, just like module globals. This made it possible do to:
def f():
from math import *
return cos(3.0)
https://peps.python.org/pep-0227/#import-used-in-function-scope https://peps.python.org/pep-0227/#import-used-in-function-sc... notes "The language reference specifies that import * may only occur in a module scope. (Sec. 6.11) The implementation of C Python has supported import * at the function scope."
Python 2.1 added static nested scopes (see the PEP 227 link), which enabled a much faster locals lookup. With that one case excluded, all of the locals can be determined at parse/byte-compile time, stored in a simple vector, and referenced by offset:
>>> z = 0
>>> def f(x):
... y = x + z
... return y * 2
...
>>> import dis
>>> dis.dis(f)
1 0 RESUME 0
2 2 LOAD_FAST 0 (x)
4 LOAD_GLOBAL 0 (z)
14 BINARY_OP 0 (+)
18 STORE_FAST 1 (y)
3 20 LOAD_FAST 1 (y)
22 LOAD_CONST 1 (2)
24 BINARY_OP 5 (*)
28 RETURN_VALUE
The "LOAD_FAST" uses offset 0 to store "x" and offset 1 to store "y", while "z" required a LOAD_GLOBAL to find z in the globals() dictionary.
- mg 2y agoYes, I stumbled across this quite recently: https://x.com/marekgibney/status/1817495719520940236 https://x.com/marekgibney/status/1817495719520940236 I was surprised that you cannot access dynamically created local variables even though they exist. Looks like the local dictionary still exists. Just that whether accessing a local variable looks it up in the local or global dictionary is determined at compile time.
- eesmith 2y agoSee PEP 667, "Consistent views of namespaces" https://peps.python.org/pep-0667/ https://peps.python.org/pep-0667/ . In Python 3.12, quoting https://docs.python.org/dev/whatsnew/3.13.html https://docs.python.org/dev/whatsnew/3.13.html > PEP 667: The locals() builtin now has defined semantics when mutating the returned mapping. Python debuggers and similar tools may now more reliably update local variables in optimized scopes even during concurrent code execution. with more details at https://docs.python.org/dev/whatsnew/3.13.html#whatsnew313-locals-semantics https://docs.python.org/dev/whatsnew/3.13.html#whatsnew313-l... , which adds: > To ensure debuggers and similar tools can reliably update local variables in scopes affected by this change, FrameType.f_locals now returns a write-through proxy to the frame’s local and locally referenced nonlocal variables in these scopes
- mg 2y agoInteresting. As I understand it, it will not affect my example: def f(): exec('y = 7') # will still create a local variable print(locals()['y']) # will still print this local variable print(y) # will still try to access a global variable 'y'
- eesmith 2y agoThat does not work for me in Python 3.10 or 3.12: Python 3.12.2 ... Type "help", "copyright", "credits" or "license" for more information. >>> def f(): ... exec('y = 7') # will still create a local variable ... print(locals()['y']) # will still print this local variable ... print(y) # will still try to access a global variable 'y' ... >>> f() 7 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in f NameError: name 'y' is not defined It does print 7 in Python 2.7.
- mg 2y agoThat's what I meant. It tries to access a global variable 'y' and fails because there is none. See also: https://x.com/marekgibney/status/1817495719520940236 https://x.com/marekgibney/status/1817495719520940236 and https://x.com/marekgibney/status/1817495721974583569 https://x.com/marekgibney/status/1817495721974583569
- deleted 2y ago[deleted]
- eesmith 2y agoI see what you mean now. Messing with locals has been a long-time danger zone. I don't know how it is supposed to interact with exec - another danger zone - and I don't know enough about the changes in 3.13 to know if it's resolved.