This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: global / nonlocal interference : is this a bug, a feature or a design hole ?
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Camion, levkivskyi, r.david.murray
Priority: normal Keywords:

Created on 2017-12-18 09:59 by Camion, last changed 2022-04-11 14:58 by admin.

Messages (21)
msg308537 - (view) Author: (Camion) Date: 2017-12-18 09:59
Hello, 

"PEP 3104 -- Access to Names in Outer Scopes" introduced the keywords "global" and "nonlocal". but didn't make clear (to me) if this behaviour is a bug, an intentional feature, or a design hole which might be considered good or bad. 

I have observed that when the nonlocal keyword gives acces to a grand parent function's variable, the presence in the parent function, of an access to a global variable with the same name, blocks it with a syntax error (SyntaxError: no binding for nonlocal 'a' found).


a = "a : global"
def f():
    a = "a : local to f"
    def g():
#        global a     # uncommenting this line causes a syntax error.
#        a = a+", modified in g"
        def h():
            nonlocal a
            a = a+", modified in h"
        h()
        print (f"in g : a = '{a}'")
    g()
    print (f"in f : a = '{a}'")
f()
print (f"glogal : a = '{a}'")
msg308570 - (view) Author: (Camion) Date: 2017-12-18 16:11
I forgot to mention, that I wonder if the interpreter shouldn't instead,
- either consider that since global a is used is g, it should be considered as also local to g from h point of view,
- or if h should be able to access f's version of a.
- (or if on the contrary, this ambiguity is the very reason to cause a syntax error, -- but in this case, one might need to change the text of the error message to make it more accurate. --)
msg308580 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-12-18 18:45
What is happening here is that what nonlocal does is give the function containing the nonlocal statement access to the "cell" in the parent function that holds the local variable of that function.  When you use a global statement, the name instead refers to a global variable, and  there is no local cell created for that variable name.  Thus you get "no binding [cell] for nonlocal 'a' found".  That is literally correct from the compiler's point of view.

I agree that this message is mysterious unless you understand how python scoping works at a fairly detailed level, and thus it would be nice if the error message could be improved.  The code generating the message may not know that there's a global statement for that name in effect, though, so it might not be trivial to improve it.

Would it be possible for nonlocal to effectively "chain", and turn the references in the inner function into global references?  In theory the answer would be yes, but in practice it might be distinctly non-trivial, and would probably be a PEP level change to the language.  It is currently documented that nonlocal does not look in to the global namespace (https://docs.python.org/3/reference/simple_stmts.html#nonlocal): "The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals.)
msg308610 - (view) Author: (Camion) Date: 2017-12-19 03:57
Well, David, I'm convinced this behavior is "logical" and is not some "logic" flaw. My question is more about the fact that it is desirable and was intentionally designed that way,or if on the contrary no one thought it might happen that way and it's not what was wished.

I understand that it might be a problem to link to a global (global a), a variable (h's a) which was declared in a non-global way, but is makes sense to have the presence of "global a" in g, block all possibility for h, to access it's grand parent's a.

In all case, I believe this should at lease be clearly documented in (or in relation with) PEP 3104 to make sure it has been decided that way, or else it will look like a design hole.
msg308611 - (view) Author: (Camion) Date: 2017-12-19 04:02
Oops, in my previous post please read : 

"but does it makes sense to have the presence of "global a" in g, block all possibility for h, to access it's grand parent's a ?"

instead of

"but is makes sense to [...] grand parent's a."
msg308623 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2017-12-19 09:45
> Does it makes sense to have the presence of "global a" in g, block all possibility for h, to access it's grand parent's a ?

From the perspective of ML-style languages with pure lexical scoping, no, it does not make sense.


But Python started with C-like simple name spaces, then nested functions and 'nonlocal' were added.


I think the answer (as usual) is that people may rely on the established convention and that it is no problem in practice.
msg308641 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-12-19 13:29
Right, it was indeed "designed that way" in the sense that nolocal was only ever intended to access variables from the surrounding local scope, *not* the global scope.  If you put a variable name in the global scope, nonlocal was not intended to be able to access it (it is then a global variable, not a local variable).

So the only question that keeps this issue open is can the error message be improved for the case where a global declaration affects the variable name in question (the message is clear when there is no variable with that name in the outer function at all).

Any change to this design would be an enhancement request and discussion of it should start on the python-ideas mailing list.
msg308642 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-12-19 13:35
When I said "the only thing keeping this issue open" is the message, I should acknowledge that you mentioned clarifying the documentation, but as I pointed out the documentation is already clear: it says nonlocal does not access variables in the global scope, and in your example 'a' is a variable in the global scope, because it is declared to be one.  We generally don't update PEPs after they are accepted and implemented; after that point the documentation is the real reference since the implementation may actually be different in detail than the PEP due to later enhancements. The PEP is still useful as an historical document.  (There are exceptions to that, but that is the general rule.)
msg308644 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2017-12-19 14:22
I guess I'd vote for closing this, because the first Google result for "no binding for nonlocal" on Stackoverflow is quite clear.

The ideal message would be "'a' cannot be both global and nonlocal", but
it would probably complicate the compiler ever so slightly.
msg308679 - (view) Author: (Camion) Date: 2017-12-19 20:08
David and Stefan, you're both missing my main point which is the fact that the presence of the global declaration in the parent (g) **blocks the access to the grand parent context**, which would be accessible without this global declaration (of another variable since that one is global. It just happen to have the same name) in g; and the stackoverflow post also ignores this question.

I do not disagree that this might be a desired feature (we might wish to reject this because of the potential confusion caused by this kind of name collision situation), but without any clear specification on it (since this point doesn't seem to have been discussed in the (or any?) PEP), it could always be challenged as a design flaw.
msg308680 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-12-19 20:28
Hmm.  I suppose that could be clarified in the docs.  I would find it very counter-intuitive for the grandparent 'a' to be accessible, which is probably why I did not consider that an issue.
msg308682 - (view) Author: (Camion) Date: 2017-12-19 20:45
My interrogation is about the fact that this doesn't seem to have been a collective decision and I'm not even sure it WAS anyone decision : it might as well have been an unintentional consequence of an implementation choice. 

So, in the "weakest" configuration, avoiding the implementation challenge for "design flaw" would require a language specification to at least mention that in this case, the behavior in undefined.
msg308683 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2017-12-19 20:47
Okay, I have never used something like that. Personally, I'd
disallow the global statement in g() if 'a' is local in f().

>>> a = 10
>>> def f():
...     a = 20
...     def g():
...         global a
...         print(a)
...     g()
... 
>>> 
>>> f()
10
msg308695 - (view) Author: Ivan Levkivskyi (levkivskyi) * (Python committer) Date: 2017-12-19 23:54
Stefan, your last example is formally speaking OK, if one reads the "Execution model" literally. The original example is however too ambiguous, so it is good that it triggers an error.

I think there is a chance to improve the error message here, but I didn't think about this carefully (in particular, a situation where `global` appears after the inner function `h` definition worries me).
msg308701 - (view) Author: (Camion) Date: 2017-12-20 02:35
Ivan, 

I believe, this sentence : "(the scope in which a new binding should be created cannot be determined unambiguously)" in https://docs.python.org/fr/3/reference/simple_stmts.html#nonlocal, is the one which should be clarified first, and then this would probably give the bases to improve the error message.
It raises the following concerns : 
  1/ Is it me or there is a nasty double negation in this sentence which make it tell the contrary of what it should ?
  2/ What are the conditions which would make such an ambiguity appear ? I wonder what the persons who wrote this had on their mind when they wrote it, but in regard with code reading/reviewing, my original example might be an example of such an ambiguous situation. Now the question is: are there others and what would they look like ?
msg308703 - (view) Author: (Camion) Date: 2017-12-20 03:51
What I mean is that we need to clarify (by giving examples ?) what make the scope for the new binding "not be unambiguously decidable", because, for an example : My example is totaly unambiguously decidable for an interpreter. It's for a human reader that it might lead to mistakes.
msg308722 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2017-12-20 11:31
> Stefan, your last example is formally speaking OK, if one reads the "Execution model" literally. The original example is however too
+ambiguous, so it is good that it triggers an error.

OK yes -- desirable, no. IMO execution models are not a great definition for scoping.

It certainly prevents Python scopes from being called "lexical scopes".
msg308730 - (view) Author: (Camion) Date: 2017-12-20 12:45
Stefan, You wrote : « It certainly prevents Python scopes from being called "lexical scopes". »

I don't understand why... Could you explain ?
msg308731 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2017-12-20 12:58
Because in the example in msg308683 true lexical scoping should,
when processing g()'s name space, search for the name 'a' in the
lexically closest scope f(), find it there and conclude that
'a' cannot be global.
msg308743 - (view) Author: (Camion) Date: 2017-12-20 16:00
I don't think there anything in the definition of "lexical scoping", which forbids to have a way to access globals from some place. Lexical scoping just means that scoping is defined in regards of the source code, by opposition dynamic scoping, which defines the scope in regard with execution.
msg308745 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2017-12-20 16:09
You can access globals, but not through another nested scope
where the global name is shadowed.

I have to excuse myself from this discussion. It's interesting,
but I don't have enough bandwidth.
History
Date User Action Args
2022-04-11 14:58:55adminsetgithub: 76542
2017-12-20 16:10:15skrahsetnosy: - skrah
2017-12-20 16:09:58skrahsetnosy: r.david.murray, skrah, levkivskyi, Camion
messages: + msg308745
2017-12-20 16:00:37Camionsetmessages: + msg308743
2017-12-20 12:58:22skrahsetmessages: + msg308731
2017-12-20 12:45:19Camionsetmessages: + msg308730
2017-12-20 11:31:39skrahsetmessages: + msg308722
2017-12-20 03:51:30Camionsetmessages: + msg308703
2017-12-20 02:35:20Camionsetmessages: + msg308701
2017-12-19 23:54:12levkivskyisetmessages: + msg308695
2017-12-19 20:47:47skrahsetmessages: + msg308683
2017-12-19 20:45:50Camionsetmessages: + msg308682
2017-12-19 20:28:58r.david.murraysetmessages: + msg308680
2017-12-19 20:08:45Camionsetmessages: + msg308679
2017-12-19 14:22:11skrahsetmessages: + msg308644
2017-12-19 13:35:45r.david.murraysetmessages: + msg308642
2017-12-19 13:29:50r.david.murraysetmessages: + msg308641
2017-12-19 09:45:32skrahsetnosy: + skrah
messages: + msg308623
2017-12-19 07:06:25serhiy.storchakasetnosy: + levkivskyi
2017-12-19 04:02:25Camionsetmessages: + msg308611
2017-12-19 03:57:54Camionsetmessages: + msg308610
2017-12-18 18:45:30r.david.murraysetmessages: + msg308580
2017-12-18 16:11:18Camionsetmessages: + msg308570
2017-12-18 14:01:59r.david.murraysetnosy: + r.david.murray
2017-12-18 09:59:49Camioncreate