[Python-Dev] Idea: Dictionary references
Steven D'Aprano
steve at pearwood.info
Fri Dec 18 07:56:04 EST 2015
On Thu, Dec 17, 2015 at 09:30:24AM -0800, Andrew Barnert via Python-Dev wrote:
> On Dec 17, 2015, at 07:38, Franklin? Lee <leewangzhong+python at gmail.com> wrote:
> >
> > The nested dictionaries are only for nested scopes (and inner
> > functions don't create nested scopes). Nested scopes will already
> > require multiple lookups in parents.
>
> I think I understand what you're getting at here, but it's a really
> confusing use of terminology. In Python, and in programming in
> general, nested scopes refer to exactly inner functions (and classes)
> being lexically nested and doing lookup through outer scopes. The fact
> that this is optimized at compile time to FAST vs. CELL vs.
> GLOBAL/NAME, cells are optimized at function-creation time, and only
> global and name have to be resolved at the last second doesn't mean
> that there's no scoping, or some other form of scoping besides
> lexical. The actual semantics are LEGB, even if L vs. E vs. GB and E
> vs. further-out E can be optimized.
In Python 2, the LOAD_NAME byte-code can return a local, even though it
normally doesn't:
py> x = "global"
py> def spam():
... exec "x = 'local'"
... print x
...
py> spam()
local
py> x == 'global'
True
If we look at the byte-code, we see that the lookup is *not* optimized
to inspect locals only (LOAD_FAST), but uses the regular LOAD_NAME that
normally gets used for globals and builtins:
py> import dis
py> dis.dis(spam)
2 0 LOAD_CONST 1 ("x = 'local'")
3 LOAD_CONST 0 (None)
6 DUP_TOP
7 EXEC_STMT
3 8 LOAD_NAME 0 (x)
11 PRINT_ITEM
12 PRINT_NEWLINE
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
> What you're talking about here is global lookups falling back to
> builtin lookups. There's no more general notion of nesting or scoping
> involved, so why use those words?
I'm not quite sure about this. In principle, every name lookup looks in
four scopes, LEGB as you describe above:
- locals
- non-locals, a.k.a. enclosing or lexical scope(s)
- globals (i.e. the module)
- builtins
although Python can (usually?) optimise away some of those lookups. The
relationship of locals to enclosing scopes, and to globals in turn,
involve actual nesting of indented blocks in Python, but that's not
necessarily the case. One might imagine a hypothetical capability for
manipulating scopes directly, e.g.:
def spam(): ...
def ham(): ...
set_enclosing(ham, spam)
# like:
# def spam():
# def ham(): ...
The adventurous or fool-hardy can probably do something like that now
with byte-code hacking :-)
Likewise, one might consider that builtins is a scope which in some
sense encloses the global scope. Consider it a virtual code block that
is outdented from the top-level scope :-)
> So, trying to generalize global vs. builtin to a general notion of
> "nested scope" that isn't necessary for builtins and doesn't work for
> anything else seems like overcomplicating things for no benefit.
Well, putting aside the question of whether this is useful or not, and
putting aside efficiency concerns, let's just imagine a hypothetical
implementation where name lookups used ChainMaps instead of using
separate LOAD_* lookups of special dicts. Then a function could set up a
ChainMap:
function.__scopes__ = ChainMap(locals, enclosing, globals, builtins)
and a name lookup for (say) "x" would always be a simple:
function.__scopes__["x"]
Of course this would be harder to optimize, and hence probably slower,
than the current arrangement, but I think it would allow some
interesting experiments with scoping rules:
ChainMap(locals, enclosing, globals, application_globals, builtins)
You could implement dynamic scoping by inserting the caller's __scopes__
ChainMap into the front of the called function's ChainMap. And attribute
lookups would be something like this simplified scope:
ChainMap(self.__dict__, type(self).__dict__)
to say nothing of combinations of the two.
So I think there's something interesting here, even if we don't want to
use it in production code, it would make for some nice experiments.
--
Steve
More information about the Python-Dev
mailing list