[Python-ideas] A "local" pseudo-function
Tim Peters
tim.peters at gmail.com
Wed May 2 15:09:48 EDT 2018
[MRAB]
>> There's another question that hasn't been asked yet: what should locals()
>> and globals() return?
[Tim, "globals()" is obvious, "locals()" can be surprising now]
> ...
And here recording the results of some code spelunking Dicts don't
really have anything to do with how locals are implemented anymore;
calling "locals()" now inside a function _constructs_ a dict on the
fly out of lower-level implementation gimmicks, to be kinda-compatible
with what locals() returned before nested scopes were introduced.
The real distinctions of interest are recorded in the code object now,
under these attributes, where I'm giving a fuller explanation than can
be found in the docs, and where "referenced" doesn't distinguish
between "binding merely retrieved" and "binding is changed":
1. co_varnames: tuple of names of locals not referenced in an
enclosed local scope
2. co_cellvars: tuple of names of locals that are referenced in an
enclosed local scope
3 .co_freevars: tuple of names local to an enclosing local scope and
referenced in this enclosed code
"locals()" now generally builds a dict out of all three of those name sources.
#1 is the only one that made sense before nested scopes were introduced.
The union of #1 and #2 is the set of names local to the code's scope;
CPython implements their bindings in different ways, so needs to
distinguish them.
The names in #3 aren't local to the code block at all (they are local
to some enclosing local scope), but for whatever reason are included
in the code's "locals()" dict anyway. I would not have included them.
For concreteness:
def disp(func):
print("for", func.__name__)
code = func.__code__
for attr in "co_varnames", "co_cellvars", "co_freevars":
print(" ", attr, getattr(code, attr))
def outer():
outer_only = 1
outer_used_inner = 2
outer_bound_by_inner = 3
def inner():
nonlocal outer_bound_by_inner
inner_only = 4
outer_bound_by_inner = 5
inner_only = outer_used_inner
inner()
disp(outer)
disp(inner)
outer()
And its output:
for outer
co_varnames ('outer_only', 'inner')
co_cellvars ('outer_bound_by_inner', 'outer_used_inner')
co_freevars ()
for inner
co_varnames ('inner_only',)
co_cellvars ()
co_freevars ('outer_bound_by_inner', 'outer_used_inner')
More information about the Python-ideas
mailing list