[Python-ideas] A "local" pseudo-function

Tim Peters tim.peters at gmail.com
Tue May 1 16:59:42 EDT 2018

>>> Imagine renaming the specified names that are declared 'local'
>>> throughout the nested portion:
>>> def f():
>>>     a = 10
>>>     local local_a:
>>>         def showa():
>>>             print("a is", local_a)
>>>         showa() # Raises NameError in showa because nothing bound to
>>> local_a yet.
>>>         local_a = 20
>>>         showa() # 20
>>>         local_a = 30
>>>     showa() # Raises NameError in f because local_a not defined.

>> In a later message I showed executable-today Python code that I
>> believe models your intent.  That code does indeed display "30" for
>> the last call, and while I myself don't see that it's _useful_ yet,
>> the model is incomprehensible if it doesn't.
>> It doesn't matter that local_a isn't defined at function scope,
>> because showa() refers to the locals _in_ the "local a:" scope.  The
>> last value bound to local_a was 30, so that's what showa() needs to
>> show.  That the name `showa` is _bound_ in f's locals has nothing to
>> do with whose locals showa() _uses_.  Other current messages about
>> "closures" go on more about that.

> Ah, yes. I see what you mean.
> There's another question that hasn't been asked yet: what should locals()
> and globals() return?

Under my meaning, the same answer as always:  exactly the same as if
"local a:' were replaced by "if True:" ;-)

Under yours, you're not going to like the answer.  It seems obvious
that since globals() refers to the module dict, nothing about that
would change.  You can add prints to my
workalike-code-under-current-Python snippet to see what locals()
_does_ return in various places.

It's not what you'd expect (that locals() inside the `local a:` block
is a dict with only key "local_a"), because of what I believe are
undocumented implementation details.  This is much easier to see in a
bare-bones example:

    def f():
        b = 12
        def g():
            nonlocal b
            b = 17
            a = 42
            print("inner", locals())
        print("outer", locals())

The output:

inner {'a': 42, 'b': 17}
outer {'g': <function f.<locals>.g at 0x0000024E28C44AE8>, 'b': 17}

That is, despite that `b` is explicitly declared as `nonlocal` in the
inner function, and does in fact belong to the outer scope, it
nevertheless shows up inside the inner function's locals().

The compiler isn't confused, but `locals()` existed long before nested
scopes were added, and, e.g., no "nonlocals()` builtin was added
later.  PEP 227 (which introduced nested scopes) explicitly declined
to add one, and said "it will not be possible to gain dictionary-style
access to all visible scopes".  I was quite surprised to see "b" as a
key in the inner locals() above!

But so long as it is, any new gimmick building on the current
implementation would inherit that behavior.

More information about the Python-ideas mailing list