global vs nonlocal statements and scopes

Posted by Sam stoltenberg on March 25, 2020
local_vs_global

global vs nonlocal statements

Defining a global variable:

In [1]:
def make_global():
    global x
    x = 10
make_global()
print(x)
10

We can also define a global variable inside of a function which could also be accessed from another function:

In [2]:
def func():
    global k
    k = 12
    
def other_func():
    print(k)

func()
other_func()
12

If I tried to do that without global I get an error:

In [3]:
def func():
    z = 12
func()
print(z)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-c726185abee6> in <module>
      2     z = 12
      3 func()
----> 4 print(z)

NameError: name 'z' is not defined

Or simply define a variable and use it inside the function.

If a variable is made outside of a function it is considered "global":

In [4]:
a_variable = 10  # inherently global
def func():
    print(a_variable)
func()
10

nonlocal is different than global. It reaches toward the higher scope, finds the variable, and works with that one.

First I will show without the nonlocal keyword:

In [5]:
def func():
    fruit = 'apple'
    def func_inner():
        fruit = 'orange'
    func_inner()  # Calling the inner function
    return fruit  # This is what is getting printed

print(func())  # apple
apple

As you can see func() returned apple. Setting it to 'orange' inside of the inner function did nothing to the original fruit. Only the inner function knew it was orange for the duration of that inner function.

Here is the same functions with the nonlocal statement

In [6]:
def func():
    fruit = 'banana'
    def func_inner():
        nonlocal fruit
        fruit = 'pear'
    func_inner()
    return fruit

print(func())  # pear
pear

You can see that the fruit was changed to 'pear' and 'banana' was overwritten.

Now if I had a global fruit 'cherry' would it be overwritten?

In [7]:
fruit = 'cherry'

def func():
    fruit = 'raspberry'
    def func_inner():
        nonlocal fruit
        fruit = 'apricot'
    func_inner()
    return fruit

print(func())  # changed to apricot
print(fruit)  # stays the same
apricot
cherry

No, as you can see the fruit variable with 'cherry' has stayed the same, and the nonlocal fruit in the scope of 'func' was changed to 'apricot'.

If it were a global variable inside 'func_inner' it would have changed the original fruit, and the 'func' fruit would go unchanged. See below:

In [8]:
fruit = 'nectarine'  # changes THERE

def func():
    fruit = 'tomato' # does not change
    def func_inner():
        global fruit
        fruit = 'avocado'  # changes HERE
    func_inner()
    return fruit  # still tomato

print(func())  # tomato
print(fruit)  # avacado
tomato
avocado

Try it for yourself!