Hopefully anyone is still reading python-dev. I'm going to try to summarize the differences between the two proposals, even though Mark already did so in his PEP. But I'd like to start by calling out the key point of contention.
Everything here is about locals() and f_locals in *function scope*. (I use f_locals to refer to the f_locals field of frame objects as seen from Python code.) And in particular, it is about what I'll call "extra variables": the current CPython feature that you can add *new* variables to f_locals that don't exist in the frame, for example: def foo(): x = 1 locals()["y"] = 2 # or sys._getframe()["y"] = 2 My first reaction was to propose to drop this feature, but I realize it's kind of important for debuggers to be able to execute arbitrary code in function code -- assignments to locals should affect the frame, but it should also be possible to create new variables (e.g. temporaries). So I agree we should keep this. Terminology-wise, I will refer to variables that are allocated in the frame (like "x" above, and including nonlocals/cells) as "proper" variables. Both PEPs give up when it comes to locals(), declaring it to return a snapshot in this case. This is mostly to ensure better backwards compatibility, since existing code calling locals() may well assume it's a dict. Both PEPs make f_locals some kind of proxy that gives a direct read-write view on the variables in the frame (including cells used for nonlocal references), but they differ in the precise semantics. So apparently the key difference of opinion between Mark and Nick is about f_locals, and what to do with extras. In Nick's proposal when you reference f.f_locals twice in a row (for the same frame object f), you get the same proxy object, whereas in Mark's proposal you get a different object each time, but it doesn't matter, because the proxy has no state other than a reference to the frame. In Mark's proposal, if you assign a value to an extra variable, it gets stored in a hidden dict field on the frame, and when you read the proxy, the contents of that hidden dict field gets included. This hidden dict lazily created on the first store to an extra variable. (Mark shows pseudo-code to clarify this; the hidden dict is stored as _extra_locals on the frame.) In Nick's proposal, there's a cache on the frame that stores both the extras and the proper variables. This cache can get out of sync with the contents of the proper variables when some bytecode is executed (for performance reasons we don't want the bytecode to keep the cache up to date on every store), so there's an operation to sync the frame cache (sync_frame_cache(), it's not defined in which namespace this exists -- is it a builtin or in sys?). Frankly the description in Nick's PEP is hard to follow -- I am not 100% sure what is meant by "the dynamic snapshot", and it's not quite clear whether proper variables are copied into the cache (and if so, why). There are also differences in the proposed C API changes, but the differences there are solvable once we choose the semantics for f_locals. Personally, I find Mark's proposed semantics for f_locals simpler -- there's no cache, only storage for extras, so there's nothing that can get out of sync. I would even consider making locals() return the same proxy -- this is simpler and more consistent with module and class scopes, but it is less backwards compatible, and locals() is used orders of magnitude more than f_locals. (Also, we'd have to modify exec() and eval() to allow using a non-dict as globals, which would require some deep changes in the interpreter.) --Guido PS. In Mark's PEP, there's a pseudo-code version of locals() that can give a different result in class scope than the current CPython implementation: Using __prepare__, a metaclass can provide a namespace to execute the class body that's not a dict (subclass) instance. The current CPython behavior and AFAICT Nick's PEP return that namespace from locals(), but Mark's pseudo-code would return a snapshot copy. I think it's better to stick to the current semantics (and I suspect Mark overlooked this edge case). On Fri, Aug 20, 2021 at 8:23 AM Mark Shannon <m...@hotpy.org> wrote: > Hi all, > > I have submitted PEP 667 as an alternative to PEP 558. > https://www.python.org/dev/peps/pep-0667/ > > Nick and I have agreed to disagree on the way to fix locals() and > f_locals. We are both in agreement that it needs fixing. > > In summary, PEP 667 has roughly the same surface behavior as PEP 558 but > is simpler and more consistent internally, at the expense of some minor > C API backwards incompatibility issues. > > PEP 558 also has backwards incompatibility issues, but claims to be more > compatible at the C API level. > > Cheers, > Mark. > _______________________________________________ > Python-Dev mailing list -- python-dev@python.org > To unsubscribe send an email to python-dev-le...@python.org > https://mail.python.org/mailman3/lists/python-dev.python.org/ > Message archived at > https://mail.python.org/archives/list/python-dev@python.org/message/4RH5YCXIHIP6MRVTCOKOOO4GKCIMH4GJ/ > Code of Conduct: http://python.org/psf/codeofconduct/ > -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/ACFNKD4TGTEXUP45QGDKSR24KVAE5YJF/ Code of Conduct: http://python.org/psf/codeofconduct/