Unify global and nonlocal
Hello, The 'global' and 'nonlocal' statements serve very similar purposes. They both allow to rebind names in an outer scope. While 'nonlocal' allows to rebind a name in the closest outer scope, *except* the global module scope, 'global' is used for the later case. Is there a reason why 'nonlocal' has to stop at the scope immediately after the global module scope? If 'nonlocal' could continue until the global scope, then it would become (almost) a generalized case of 'global'. Having both statements goes against the "There should be one - and preferably only one - obvious way to do it" principle. It's also confusing for beginners and people used to a functional style. I think 'nonlocal' should be changed to support all the enclosing scopes, including the global module scope, and the 'global' statement should be marked as deprecated. There is one specific use case that breaks though, which is rebinding to global names from a deep function skipping names in the middle, for example: x = 'global' def outer1(): x = 'outer1' def outer2(): global x x = 'outer2' # binds the global module name But you know "special cases aren't special enough to break the rules". My apologizes if this topic was already discussed in the past. Manuel.
Manuel, You might want to read over PEP 3104: http://www.python.org/dev/peps/pep-3104/ especially the section about backward compatibility. Skip
global and nonlocal are different specifically because they do different things. Yes, there is a reason why nonlocal stops at the first scope up - because that's what it exists to do. On Mon Feb 03 2014 at 12:28:42 PM, Manuel Cerón <ceronman@gmail.com> wrote:
Hello,
The 'global' and 'nonlocal' statements serve very similar purposes. They both allow to rebind names in an outer scope. While 'nonlocal' allows to rebind a name in the closest outer scope, *except* the global module scope, 'global' is used for the later case.
Is there a reason why 'nonlocal' has to stop at the scope immediately after the global module scope? If 'nonlocal' could continue until the global scope, then it would become (almost) a generalized case of 'global'. Having both statements goes against the "There should be one - and preferably only one - obvious way to do it" principle. It's also confusing for beginners and people used to a functional style.
I think 'nonlocal' should be changed to support all the enclosing scopes, including the global module scope, and the 'global' statement should be marked as deprecated.
There is one specific use case that breaks though, which is rebinding to global names from a deep function skipping names in the middle, for example:
x = 'global' def outer1(): x = 'outer1' def outer2(): global x x = 'outer2' # binds the global module name
But you know "special cases aren't special enough to break the rules".
My apologizes if this topic was already discussed in the past.
Manuel. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, Feb 03, 2014 at 08:40:08PM +0000, Amber Yust wrote:
global and nonlocal are different specifically because they do different things. Yes, there is a reason why nonlocal stops at the first scope up - because that's what it exists to do.
+1 "There should be one obvious way to do it" doesn't apply here, because there is no *it*, there is *them*. Two distinct things to do, rather than one: - enable writing to the top-level global namespace; - enable writing to some enclosing non-local scope. I say *some enclosing* scope rather than *the enclosing scope* because you can have more than one. The nonlocal keyword enables writing to the nearest enclosing scope which already contains the given name, not necessarily the closest enclosing scope: py> def outer(): ... a = "outer" ... def inner(): ... def innermost(): ... nonlocal a ... a = "modified" ... innermost() ... print("Before:", a) ... inner() ... print("After:", a) ... py> outer() Before: outer After: modified The global namespace is special, and special enough to justify being handled specially. Of course, it is possible to generalise the idea that globals are "just" a case of lexical scoping, and there is some truth to that. But it's an over-generalisation, a bit like deciding that since the built-in len() function is just a many-to-one transformation of arbitrary sequences to integers, it belongs as a codec: import codecs length_of_list = codecs.encode(my_list, "len") In the case of globals and non-locals: * Non-locals always have an enclosing function, globals do not. * Non-locals and globals are typically used for different purposes, by users at different levels of expertise. Writing to a non-local scope using the nonlocal keyword tends to be done only by advanced users for advanced purposes; writing to global variables using the global keyword tends to be done only by beginners who haven't yet learned that global variables are considered harmful. * A corrolary of the above is that it is difficult to think of any use-cases where you don't know at edit-time whether you intend to write to a variable in the global scope or a non-local scope. I can't think of any case where I wouldn't know whether to declare something global or nonlocal ahead of time, so there's no advantage to having a single keyword handle both cases. -- Steven
On 4 February 2014 11:23, Steven D'Aprano <steve@pearwood.info> wrote:
In the case of globals and non-locals:
* Non-locals always have an enclosing function, globals do not.
* Non-locals and globals are typically used for different purposes, by users at different levels of expertise. Writing to a non-local scope using the nonlocal keyword tends to be done only by advanced users for advanced purposes; writing to global variables using the global keyword tends to be done only by beginners who haven't yet learned that global variables are considered harmful.
* A corrolary of the above is that it is difficult to think of any use-cases where you don't know at edit-time whether you intend to write to a variable in the global scope or a non-local scope. I can't think of any case where I wouldn't know whether to declare something global or nonlocal ahead of time, so there's no advantage to having a single keyword handle both cases.
There's also a critical operational distinction: 'nonlocal' demands that it be able to find the name at compile time, while 'global' will happily create a *new* name binding at module global scope. This difference in intent allows an appropriate error message to be generated when nonlocal is used incorrectly: >>> def outer(): ... a_name = 1 ... def inner(): ... nonlocal a_nme ... a_name += 2 ... return a_name ... return inner ... SyntaxError: no binding for nonlocal 'a_nme' found By contrast, global will happily bind new names, because the compiler makes no assumptions about what names may eventually show up at module global scope at run time:
def f(): ... global a_name ... a_name += 2 ... return a_name ... f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f NameError: global name 'a_name' is not defined a_name = 1 f() 3
While the difference isn't often obvious, function namespaces are actually *very* special beasts in Python, compared to the relatively straightforward behaviour of module and the default class namespaces as key:value mappings. This is reflected both in the additional restrictions that apply to function namespaces (like not allowing star imports in Python 3), as well as in the fact that they are accessed via nonlocal, while the module namespace is accessed (for rebinding purposes) via the global statement. It's also why mutating the mapping returning by globals() is fully supported, while mutating the result of locals() is explicitly declared as having undefined behaviour (it may or may not affect the current scope either temporarily or permanently, depending on implementation details). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Feb 3, 2014, at 12:27, Manuel Cerón <ceronman@gmail.com> wrote:
Is there a reason why 'nonlocal' has to stop at the scope immediately after the global module scope? If 'nonlocal' could continue until the global scope, then it would become (almost) a generalized case of 'global'. Having both statements goes against the "There should be one - and preferably only one - obvious way to do it" principle. It's also confusing for beginners and people used to a functional style.
In my experience dealing with people coming to python from functional languages, they generally want closure variables to be as restricted as possible, and it's actually the fact that you can use a global as a closure variable _at all_ that they find confusing, not the fact that the syntax and semantics are a bit different than for a local closure variable. In fact, there was a question on StackOverflow a few weeks ago from someone who wanted to know how this could possibly work: a = [] def spam(): return a[0] a.append(1) print spam.__closure__ print spam() (Yes, it was Python 2, and it's an implicit rather than explicit global... But same idea.) I don't think this change would help people with that kind of confusion at all. That being said, I don't think it would _hurt_ them, and it would probably help novices learning the concept of closures for the first time, in Python. And it could make some kinds of refactoring easier to mechanize.
participants (6)
-
Amber Yust -
Andrew Barnert -
Manuel Cerón -
Nick Coghlan -
Skip Montanaro -
Steven D'Aprano