What’s the difference between global and nonlocal in Python?

Asked

Viewed 4,503 times

22

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

  • What is the difference between the two statements?
  • When to use each?

1 answer

21


The functioning of both statements is quite similar, what changes is the scope for each reference. The statement global always refers to the overall scope, that is, the scope of the programme itself, while the nonlocal references the local scope above the current scope. Confusing? Let’s go to a practical example.

Let’s consider a decorator function that only increments a counter whenever a function is decorated by it and, whenever the decorated function is executed, must 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:

inserir a descrição da imagem aqui

Where the red rectangle delimits the overall scope, the blue rectangle delimits the scope of the decorating function and the green rectangle the local scope of the function wrapper.

When running the program, we get the error message on line 10:

Unboundlocalerror: local variable 'decorated_functions' referenced before assignment

The error is due to the fact that we are trying to increment the value of the variable decorated_functions inside the decorator function, but this variable is not defined for writing in this scope (blue). Since the variable is defined in the global scope (red), we can inform the interpreter that he should seek the reference for this variable in the global scope by:

global decorated_functions

Thus remaining:

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

        # 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

This way, the error on line 10 is fixed, but if we try to run the code again, we get the following error on 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 is the difference between the global and the nonlocal. If we try to import the variable last_call for the scope using global, the interpreter will fetch the variable in the red scope, giving a new error saying that the variable is not defined, precisely because the variable last_call was not defined in the red scope, but in the blue. To inform the interpreter that, instead of searching in the global scope, he must search for the 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 by setting the variable a scope above, blue scope, causing the code to run without errors.

When to use each?

As each statement references a different scope, it will depend on where your variable is declared. If it is in the overall 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 take 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, site in foo, with value 2. The internal function bar imports the global scope variable, with global, and the function baz imports the variable of the local scope of foo, with nonlocal, so the output of this code will be:

bar: 1
baz: 2

See running on Ideone | Repl.it

Additional reading

Browser other questions tagged

You are not signed in. Login or sign up in order to post.