[Python-ideas] Give nonlocal the same creating power as global

Terry Reedy tjreedy at udel.edu
Mon Sep 11 11:51:46 EDT 2017


On 9/11/2017 10:03 AM, João Matos wrote:
> 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.

The global declaration does not create anything, but it redirects 
subsequent binding.

> 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.

'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.

> 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()

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.

> 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()

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.

> 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.

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




More information about the Python-ideas mailing list