Give nonlocal the same creating power as global

Hello, I would like to suggest that nonlocal should be given the same creating power as global. If I do global a_var it creates the global a_var if it doesn't exist. I think it would be great that nonlocal maintained that power. This way when I do nonlocal a_var it would create a_var in the imediate parent environment, if it didn't exist. Without nonlocal creation powers I have to create global variables or local variables after master=Tk() (in the following example): from tkinter import StringVar, Tk from tkinter.ttk import Label def start_gui(): def change_label(): _label_sv.set('Bye Bye') def create_vars(): global _label_sv _label_sv = StringVar(value='Hello World') def create_layout(): Label(master, textvariable=_label_sv).grid() def create_bindings(): master.bind('<Escape>', lambda _: master.destroy()) master.bind('<Return>', lambda _: change_label()) master = Tk() create_vars() create_layout() create_bindings() master.mainloop() if __name__ == '__main__': start_gui() With nonlocal creation powers it would become a start_gui local variable (no global) but I could have a function to create the vars instead of having to add them after master=Tk(): from tkinter import StringVar, Tk from tkinter.ttk import Label def start_gui(): def change_label(): label_sv.set('Bye Bye') def create_vars(): nonlocal label_sv label_sv = StringVar(value='Hello World') def create_layout(): Label(master, textvariable=label_sv).grid() def create_bindings(): master.bind('<Escape>', lambda _: master.destroy()) master.bind('<Return>', lambda _: change_label()) master = Tk() create_vars() create_layout() create_bindings() master.mainloop() if __name__ == '__main__': start_gui() I know that I could also do it with OOP, but this way is more concise (OOP would add more lines and increase the lines length, which I personally dislike) This example is very simple, but if you imagine a GUI with several widgets, then the separation between vars, layout and bindings becomes useful for code organization. Best regards, João Matos

... I think this is a bad idea overall. It breaks encapsulation. I would suggest you use create_vars() to return a context: context = create_vars() create_layout(context) create_bindings(context) Or use a class: class TkView: def __init__(self): self.tk = Tk() self.create_layout() self.create_bindings() def create_layout(self): # use self.tk def create_bindings(self): # use self.tk

On 9/11/2017 10:03 AM, João Matos wrote:
The global declaration does not create anything, but it redirects subsequent binding.
'Creating new variables' was discussed and rejected when nonlocal was added. That may partly be for technical reasons of not nonlocal is implemented. But there are also problems of ambiguity. Consider this currently legal code. def f(a): def g(): pass def h(): nonlocal a a = 1 You proposal would break all such usages that depend on skipping the immediate parent environment. 'nonlocal a' effectively means 'find the closest function scope with local name a' and I strongly doubt we will change that. If you want 'nonlocal a' to bind in g, explicitly add a to g's locals, such as with 'a = None'.
Without nonlocal creation powers I have to create global variables or local variables after master=Tk() (in the following example):
There is nothing wrong with either.
In the version above, you could simplify by removing start_gui and put the operative code from 'master = Tk()' on down in the main clause. This is standard practice for non-OOP tkinter code.
Initializing the outer function local, here adding 'label_sv = None', is the price of wanting to create a class with functions instead of a class definition.
This is what classes are for. Either use 'class' or explicitly name the local of the outer function acting as a class. -- Terry Jan Reedy

Hello, You're correct. The idea is to give nonlocal the same ability, redirect subsequent bindings if the variable doesn't exist. No, what I said is that it would only create the var if it didn't exist. That means that the current behaviour of nonlocal to check all previous envs (except global) would be the same. The variable would only be created if it ididn't exist in all parent envs. The downside of the global example is that it is "poluting" the globals. The reason it is inside a function is that it is called from another module. The main module does some checks and only if the checks are ok it calls the gui module. Best regards, João Matos On 11-09-2017 16:51, Terry Reedy wrote:

On 12 September 2017 at 03:32, João Matos <jcrmatos@gmail.com> wrote:
The issue you're facing is that optimised local variables still need to be defined in the compilation unit where they're locals - we're not going to make the compiler keep track of all the nonlocal declarations in nested functions and infer additional local variables from those. (It's not technically impossible to do that, it just takes our already complex name scoping rules, and makes them even more complex and hard to understand). So in order to do what you want, you're going to need to explicitly declare a local variable in the scope you want to write to, either by assigning None to it (in any version), or by using a variable annotation (in 3.6+). Future readers of your code will thank you for making the widget definitions easier to find, rather than having them scattered through an arbitrarily large number of nested functions :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Sep 12, 2017 at 8:54 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Or, yaknow, the OP could actually use a class, instead of treating a closure as a poor-man's class... Honestly, I don't see much advantage to the closure here. A class is a far better tool for this job IMO. ChrisA

... I think this is a bad idea overall. It breaks encapsulation. I would suggest you use create_vars() to return a context: context = create_vars() create_layout(context) create_bindings(context) Or use a class: class TkView: def __init__(self): self.tk = Tk() self.create_layout() self.create_bindings() def create_layout(self): # use self.tk def create_bindings(self): # use self.tk

On 9/11/2017 10:03 AM, João Matos wrote:
The global declaration does not create anything, but it redirects subsequent binding.
'Creating new variables' was discussed and rejected when nonlocal was added. That may partly be for technical reasons of not nonlocal is implemented. But there are also problems of ambiguity. Consider this currently legal code. def f(a): def g(): pass def h(): nonlocal a a = 1 You proposal would break all such usages that depend on skipping the immediate parent environment. 'nonlocal a' effectively means 'find the closest function scope with local name a' and I strongly doubt we will change that. If you want 'nonlocal a' to bind in g, explicitly add a to g's locals, such as with 'a = None'.
Without nonlocal creation powers I have to create global variables or local variables after master=Tk() (in the following example):
There is nothing wrong with either.
In the version above, you could simplify by removing start_gui and put the operative code from 'master = Tk()' on down in the main clause. This is standard practice for non-OOP tkinter code.
Initializing the outer function local, here adding 'label_sv = None', is the price of wanting to create a class with functions instead of a class definition.
This is what classes are for. Either use 'class' or explicitly name the local of the outer function acting as a class. -- Terry Jan Reedy

Hello, You're correct. The idea is to give nonlocal the same ability, redirect subsequent bindings if the variable doesn't exist. No, what I said is that it would only create the var if it didn't exist. That means that the current behaviour of nonlocal to check all previous envs (except global) would be the same. The variable would only be created if it ididn't exist in all parent envs. The downside of the global example is that it is "poluting" the globals. The reason it is inside a function is that it is called from another module. The main module does some checks and only if the checks are ok it calls the gui module. Best regards, João Matos On 11-09-2017 16:51, Terry Reedy wrote:

On 12 September 2017 at 03:32, João Matos <jcrmatos@gmail.com> wrote:
The issue you're facing is that optimised local variables still need to be defined in the compilation unit where they're locals - we're not going to make the compiler keep track of all the nonlocal declarations in nested functions and infer additional local variables from those. (It's not technically impossible to do that, it just takes our already complex name scoping rules, and makes them even more complex and hard to understand). So in order to do what you want, you're going to need to explicitly declare a local variable in the scope you want to write to, either by assigning None to it (in any version), or by using a variable annotation (in 3.6+). Future readers of your code will thank you for making the widget definitions easier to find, rather than having them scattered through an arbitrarily large number of nested functions :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Sep 12, 2017 at 8:54 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Or, yaknow, the OP could actually use a class, instead of treating a closure as a poor-man's class... Honestly, I don't see much advantage to the closure here. A class is a far better tool for this job IMO. ChrisA
participants (5)
-
Chris Angelico
-
Jason H
-
João Matos
-
Nick Coghlan
-
Terry Reedy