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

MRAB python at mrabarnett.plus.com
Mon Apr 30 19:19:36 EDT 2018


On 2018-04-30 21:41, Tim Peters wrote:
> [MRAB <python at mrabarnett.plus.com>]
> > I think it should be lexically scoped.
>
> That's certainly arguable, but that's why I like real-code driven
> design:  abstract arguments never end, and often yield a dubious
> in-real-life outcome after one side is worn out and the other side
> "wins" by attrition ;-)
>
>
> > The purpose of 'local' would be to allow you to use a name that _might_ be
> > used elsewhere.
> >
> > The problem with a dynamic scope is that you might call some global function
> > from within the local scope, but find that it's "not working correctly"
> > because you've inadvertently shadowed a name that the function refers to.
>
> Already explained at excessive length that there's nothing akin to
> "dynamic scopes" here, except that both happen to restore a previous
> binding at times.  That's a shallow coincidence.  It's no more
> "dynamic scope" than that
>
>      savea = a
>      try:
>          a += 1
>          f(a)
>      finally:
>          a = savea
>
> is "dynamic scoping".  It's merely saving/restoring a binding across a
> block of code.
>
>
> > Imagine, in a local scope, that you call a global function that calls 'len',
> > but you've shadowed 'len'...
>
> I'm not clear on whether you picked the name of a builtin to make a
> subtle point not spelled out, but I don't think it matters.
> Regardless of whether `len` refers to a builtin or a module global
> inside your global function now, the _current_
>
> def f():
>       len = 12
>       global_function()
>
> has no effect at all on the binding of `len` seen inside
> `global_function`.  Because my understanding of "local:" changes
> absolutely nothing about Python's current scope rules, it's
> necessarily the case that the same would be true in:
>
> def f():
>      local len:
>          len = 12
>          call_something()
>
> The only difference from current semantics is that if
>
>      print(len)
>
> were added after the `local:` block, UnboundLocalError would be raised
> (restoring the state of the function-local-with-or-without-'local:'
> `len` to what it was before the block).
>
> To have "local:" mean "new nested lexical scope" instead requires
> specifying a world of semantics that haven't even been mentioned yet.
>
> In Python today, in the absence of `global` and `nonlocal`
> declarations, the names local to a given lexical scope are determined
> entirely by analyzing binding sites.  If you intend something other
> than that, then it needs to be spelled out.  But if you intend to keep
> "and names appearing in binding sites are also local to the new
> lexical scope", I expect that's pretty much useless.   For example,
>
>      def f():
>          ...
>          local x. y:
>              x = a*b
>              y = a/b
>              r1, r2 = x+y, x-y
>
> That is, the programmer surely doesn't _intend_ to throw away r1 and
> r2 when the block ends.  If they have to add a
>
>          nonlocal r1, r2
>
> declaration at the top of the block, maybe it would work as intended.
> But it still wouldn't work unless `r1` and `r2` _also_ appeared in
> binding sites in an enclosing lexical scope.  If they don't, you'd get
> a compile-time error like
The intention is that only the specified names are local.

After all, what's the point of specifying names after the 'local' if 
_any_ binding in the local scope was local?

> SyntaxError: no binding for nonlocal 'r1' found
>
> To be more accurate, the message should really say "sorry, but I have
> no idea in which scope you _intend_ 'r1' to live, because the only way
> I could know that is to find a binding site for 'r1', and I can't find
> any except inside _this_ scope containing the 'nonlocal'".  But that's
> kind of wordy ;-)
>
> If you agree that makes the feature probably unusable, you don't get
> off the hook by saying "no, unlike current Python scopes, binding
> sites have nothing to do with what's local to a new lexical scope
> introduced by 'local:'".  The same question raised in the example
> above doesn't go away:  in which scope(s) are 'r1' and 'r2' to be
> bound?
Any binding that's not specified as local is bound in the parent scope:

local b:
     local c:
         c = 0 # Bound in the "local c" scope.
         b = 0 # Bound in the "local b" scope.
         a = 0 # Bound in the main scope (function, global, whatever)
> There's more than one plausible answer to that, but in the absence of
> real use cases how can they be judged?
>
> Under "'local:' changes nothing at all about Python's scopes", the
> answer is obvious:  `r1` and `r2` are function locals (exactly the
> same as if "local:" hadn't been used).  There's nothing new about
> scope to learn, and the code works as intended on the first try ;-)
> Of course "local:" would be a misleading name for the construct,
> though.
>
> Going back to your original example, where a global (not builtin)
> "len" was intended:
>
>      def f():
>          global len  # LINE ADDED HERE
>          local len:
>              len = 12
>              global_function()
>
> yes, in _that_ case the global-or-builtin "len" seen inside
> `global_function` would change under my "nothing about scoping
> changes" reading, but would not under your reading.
>
> That's worth _something_ ;-)  But without fleshing out the rules for
> all the other stuff (like which scope(s) own r1 and r2 in the example
> above) I can't judge whether it's worth enough to care.  All the
> plausibly realistic use cases I've considered don't _really_ want a
> full-blown new scope (just robust save/restore for a handful of
> explicitly given names), and the example just above is contrived in
> comparison.  Nobody types "global len" unless they _intend_ to rebind
> the global `len`, in which case i'm happy to let them shoot both feet
> off ;-)
>
> In any case, nothing can change the binding of the builtin "len" short
> of mucking directly with the mapping object implementing builtin
> lookups.
>
> Note:  most of this doesn't come up in most other languages because
> they require explicitly declaring in which scope a name lives.
> Python's "infer that in almost all cases instead from examining
> binding sites" has consequences.
>
Would/should it be possible to inject a name into a local scope? You 
can't inject into a function scope, and names in a function scope can be 
determined statically (they are allocated slots), so could the same kind 
of thing be done for names in a local scope?


More information about the Python-ideas mailing list