Tuesday, August 6, 2019

nonlocal keyword

The nonlocal keyword is used to allow an inner function to access variables defined in an outer function. Without using the nonlocal keyword a new variable will be created in the inner function and will have no effect on the outer function's variables.

The below example demonstrates this by defining two versions of s. The first s is accessible only to the outer function say_hello and the other s is only accessible to the inner function say_it.


def say_hello():
    s = "Hello from outer function say_hello"
    def say_it():
        s = "Hello from inner function say_it"
        print(s)
    print(s)
    say_it()

say_hello()

The below code demonstrates how to access the outer function's local variable s. By using the nonlocal keyword, say_hello and say_it are sharing the variable s.


def say_hello():
    s = "Hello from outer function say_hello"
    def say_it():
        nonlocal s
        s = "Hello from inner function say_it"
    
    print(s) #print s before it is modified by say_it
    say_it() #change the value of s
    print(s) #print the new value after its modified by say_it

say_hello()

The concept is very similar to the usage of the global keyword. The difference is global is used to allow a function access to variables defined as global ( actually module attributes ) while nonlocal allows access to variables defined in outer functions.

Monday, August 5, 2019

Importing Global Variables in Python

Global variables in python are actually considered module level variables or module attributes and are not attached to a global namespace. This leads to some confusion when trying to change them from another module.

Attempting to change them can lead to some quirky behavior. This is one of the few flaws that I see in an otherwise elegant and well implemented programming language.

To demonstrate a common trap in python module attributes make a new file called hello.py and type the code below:

g_hello = "Hello World"

def print_g_hello():
    print(g_hello)
hello.py - declaring g_hello global and print_g_hello function

When you import hello into another python script you will be able to access the variable g_hello and it seems like you can change it but really your changes only take effect within your current script. Changes to g_hello will not effect the value within hello.py or other modules that you import the global with.The below code will demonstrate the problem.


from hello import g_hello, print_g_hello

print(g_hello)
g_hello = "test" #this seems to work but actually does not effect g_hello outside of this script
print(g_hello)

#using the keyword global still has no effect on the global
#it is only changed within the context of this script.
def change_hello():
    global g_hello
    g_hello = "Hello changed!"

change_hello()
print(g_hello)  #again hello changed local to this script
print_g_hello() #calls a function in hello to print g_hello showing it is unchanged
test.py - importing g_hello and print_g_hello to demonstrate global scope

In the above script we import the global using the syntax from hello import g_hello. This seems like the most common sense approach to access a global declared in another python script. However, you will get read-only access to that global variable. If that is all you need, which is usually the case with config scripts, there is no issue.

Problems arise when you find yourself trying to modify the values. As shown in the above example you will actually end up with two different values depending on how or where the variable is called.

You can change the variable if you access it using the module name instead of importing the variable separately. This is a little strange as you would expect the import statement to simply serve, like other languages, as an alias. However, importing an entire module will access its attributes directly while importing each attribute individually in fact creates a copy of that attribute that does not modify the module.

Create a new file called test2.py to demonstrate changing the global variable g_hello.

import hello #import the module so we can access its globals
from hello import print_g_hello #import the function to test if it has been changed

print(hello.g_hello)
hello.g_hello = "World"
print(hello.g_hello) #hello is changed

print_g_hello() #hello is changed within the hello module as well
test2.py - importing g_hello and print_g_hello to demonstrate changing a global

This behavior is actually intended by the makers of python to try and curb the problems with global scope. Usage of global variables are frowned upon in most programming circles and module level variables are not any better as they just attach a namespace to a globally scoped variable.

The most common usage of globals in python is for configuration files. This usage is usually condoned because passing configuration to every module or class in your solution would unnecessarily add to the code and complexity of your code.


nonlocal keyword

The nonlocal keyword is used to allow an inner function to access variables defined in an outer function. Without using the nonlocal keywo...