What is the difference of global and nonlocal in Python?

9

In Python, there are two statements that are used to reference variables external to the local scope: global and nonlocal .

  • What's the difference between the two statements?
  • When to use each?
asked by anonymous 27.10.2017 / 23:10

1 answer

9

The operation of both statements is very similar, what changes is the scope for which each reference. The statement global always refers to the global scope, that is, the scope of the program itself, while the nonlocal reference the local scope above the current scope. Confused? Let's take a practical example.

Consider a decorator function that only increments a counter whenever a function is decorated by it, and whenever the decorated function is executed, it should store the current time in a variable.

import time

# Define o contador de funções decoradas:
decorated_functions = 0

# Definição do decorador:
def decorator(function):

    # Incrementa o contador:
    decorated_functions += 1

    # Inicializa o horário da última chamada:
    last_call = None

    # Define a função de retorno:
    def wrapper(*args, **kwargs):

        # Exibe e atualiza o horário da última chamada:
        print("Última chamada:", last_call)
        last_call = time.time()

        # Chama a função decorada:
        function(*args, **kwargs)

    return wrapper

See running on Ideone | Repl.it

Graphically, we can draw the three scopes as follows:

Wheretheredrectangledelimitstheglobalscope,thebluerectangledelimitsthescopeofthedecoratorfunctionandthegreenrectanglethelocalscopeofthewrapperfunction.

Whenweruntheprogram,wegettheerrormessageonline10:

  

UnboundLocalError:localvariable'decorated_functions'referencedbeforeassignment

Theerrorisduetothefactthatwearetryingtoincrementthevalueofthedecorated_functionsvariablewithinthedecoratorfunction,butthisvariableisnotdefinedforwritinginthisscope(blue).Sincethevariableisdefinedintheglobalscope(red),wecantelltheinterpreterthatitshouldfetchthereferenceforthisvariableintheglobalscopebydoing:

globaldecorated_functions

Lookinglikethis:

importtime#Defineocontadordefunçõesdecoradas:decorated_functions=0#Definiçãododecorador:defdecorator(function):#Importaparaoescopolocalavariávelglobal:globaldecorated_functions#Incrementaocontador:decorated_functions+=1#Inicializaohoráriodaúltimachamada:last_call=None#Defineafunçãoderetorno:defwrapper(*args,**kwargs):#Exibeeatualizaohoráriodaúltimachamada:print("Última chamada:", last_call)
        last_call = time.time()

        # Chama a função decorada:
        function(*args, **kwargs)

    return wrapper

See running on Ideone | Repl.it

In this way, the error in line 10 is corrected, but if we try to execute the code again, we will have the following error in line 22:

  

UnboundLocalError: local variable 'last_call' referenced before assignment

Again, the error occurs because we are trying to access a variable that does not belong to the scope. Here the difference between global and nonlocal lives. If we try to import the variable last_call to the scope using global , the interpreter will fetch the variable in red scope, giving a new error saying that the variable is not defined, precisely because the last_call variable was not defined in the scope red, but in blue. To inform the interpreter that, instead of looking at the global scope, it should search by reference to the variable a scope above, we use the declaration nonlocal , getting:

import time

# Define o contador de funções decoradas:
decorated_functions = 0

# Definição do decorador:
def decorator(function):

    # Importa para o escopo local a variável global:
    global decorated_functions

    # Incrementa o contador:
    decorated_functions += 1

    # Inicializa o horário da última chamada:
    last_call = None

    # Define a função de retorno:
    def wrapper(*args, **kwargs):

        # Importa para o escopo local a variável:
        nonlocal last_call

        # Exibe e atualiza o horário da última chamada:
        last_call = time.time()
        print("Última chamada:", last_call)

        # Chama a função decorada:
        function(*args, **kwargs)

    return wrapper

@decorator
def foo():
    print("Foo executada")

foo()

See running on Ideone | Repl.it

In this way, the interpreter will search for the definition of the variable a scope above, blue scope, causing the code to execute without errors.

When to use each?

Because each statement refers to a different scope, it will depend on where your variable is declared. If it is in global scope, use global ; if the variable is local, within a scope above where you will use it, use nonlocal .

To make the difference clear, let's look at a very hypothetical example:

x = 1

def foo():
    x = 2

    def bar():
        global x
        print("bar:", x)

    def baz():
        nonlocal x
        print("baz:", x)

    bar()
    baz()

foo()

We have a variable x , global, with value 1 and a variable x , local in foo , with value 2. The internal function bar imports the global scope variable, with global and the baz function imports the local scope variable of foo , with nonlocal , so the output of this code will be:

bar: 1
baz: 2

See running on Ideone | Repl.it

Further reading

27.10.2017 / 23:10