[Python-Dev] [SPAM?] Re: PEP 558: Defined semantics for locals()

Nathaniel Smith njs at pobox.com
Tue May 28 21:50:04 EDT 2019

On Tue, May 28, 2019 at 6:02 PM Guido van Rossum <guido at python.org> wrote:
> On Tue, May 28, 2019 at 5:25 PM Greg Ewing <greg.ewing at canterbury.ac.nz> wrote:
>> Terry Reedy wrote:
>> > I believe that the situation is or can be thought of as this: there is
>> > exactly 1 function locals dict.  Initially, it is empty and inaccessible
>> > (unusable) from code.  Each locals() call updates the dict to a current
>> > snapshot and returns it.
>> Yes, I understand *what's* happening, but not *why* it was designed
>> that way. Would it really be prohibitively expensive to create a
>> fresh dict each time?
> No. But it would be inconsistent with the behavior at module level.
> FWIW I am leaning more and more to the [proxy] model, where locals() and frame.f_locals are the same object, which *proxies* the fast locals and cells. That only has one downside: it no longer returns a dict, but merely a MutableMapping. But why would code care about the difference? (There used to be some relevant builtins that took dicts but not general MutableMappings -- but that has been fixed long ago.)

Related trivia: the exec() and eval() builtins still mandate that
their 'globals' argument be an actual no-fooling dict, but their
'locals' argument is allowed to be any kind of mapping object. This is
an intentional, documented feature [1]. And inside the exec/eval,
calls to locals() return whatever object was passed. For example:

>>> exec("print(type(locals()))", {}, collections.ChainMap())
<class 'collections.ChainMap'>

So technically speaking, it's already possible for locals() to return
a non-dict.

Of course this is incredibly uncommon in practice, so existing code
doesn't necessarily take it into account. But it's some kind of
conceptual precedent, anyway.


[1] See https://docs.python.org/3/library/functions.html#eval and the
'exec' docs right below it. I think the motivation is that in the
current CPython implementation, every time you access a global it does
a direct lookup in the globals object, so it's important that we do
this lookup as fast as possible, and forcing the globals object to be
a actual dict allows some optimizations. For locals, though, we
usually use the "fast locals" mechanism and the mapping object is
mostly vestigial, so it doesn't matter how fast lookups are, so we can
support any mapping.

Nathaniel J. Smith -- https://vorpus.org

More information about the Python-Dev mailing list