[MRAB
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 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? 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.