Escopo de Variáveis em Python
O escopo de uma variável define o contexto em que ela pode ser utilizada. No Python, existem dois tipos principais de escopo: global e local.
Escopo global
O escopo global indica que uma variável é global, ou seja, ela é definida fora de qualquer função e está acessível a todas as funções do programa. Por exemplo:
name = "Tom"
  def say_hi():
      print("Hello", name)
  def say_bye():
      print("Goodbye", name)
  say_hi()
  say_bye()Aqui, a variável name é global e tem escopo global. Ambas as funções definidas podem usá-la livremente.
Escopo local
Ao contrário das variáveis globais, uma variável local é definida dentro de uma função e só pode ser acessada por essa função, tendo, portanto, um escopo local:
def say_hi():
    name = "Sam"
    surname = "Johnson"
    print("Hello", name, surname)
def say_bye():
    name = "Tom"
    print("Goodbye", name)
say_hi() # Hello Sam Johnson
say_bye() # Goodbye TomNesse exemplo, em cada uma das funções é definida uma variável local name. Embora essas variáveis tenham o mesmo nome, elas são variáveis distintas e estão acessíveis apenas dentro de suas respectivas funções. Além disso, na função say_hi(), a variável surname também é local, portanto, não pode ser usada na função say_bye().
Ocultando Variáveis Globais
name = "Tom"
def say_hi():
    name = "Bob"  # oculta a variável global
    print("Hello", name)
def say_bye():
    print("Goodbye", name)
say_hi()    # Hello Bob
say_bye()   # Goodbye TomNeste caso, a variável global name foi definida. No entanto, dentro da função say_hi(), há uma variável local com o mesmo nome. A função say_bye() usa a variável global, enquanto a função say_hi() utiliza a variável local, ocultando a variável global.
Se desejarmos alterar a variável global dentro de uma função, em vez de definir uma nova variável local, precisamos usar a palavra-chave global:
name = "Tom"
def say_hi():
    global name
    name = "Bob"  # altera o valor da variável global
    print("Hello", name)
def say_bye():
    print("Goodbye", name)
say_hi()    # Hello Bob
say_bye()   # Goodbye Bobnonlocal
A palavra-chave nonlocal vincula um identificador a uma variável de um contexto envolvente mais próximo, exceto o global. Normalmente, nonlocal é usado em funções aninhadas, quando é necessário referenciar uma variável ou parâmetro da função externa. Vejamos um exemplo onde isso pode ser útil:
def outer():  # função externa
    n = 5
    def inner():  # função interna
        print(n)
    inner()  # 5
    print(n)
outer()  # 5Aqui, a função interna inner() imprime o valor da variável n, que foi definida na função externa outer(). Quando chamamos outer(), veremos o número 5 duas vezes no console. No entanto, a função inner() apenas acessa o valor da variável.
Agora, vejamos o que acontece quando a função interna atribui um novo valor à variável n:
def outer():  # função externa
    n = 5
    def inner():  # função interna
        n = 25
        print(n)
    inner()  # 25
    print(n)
outer()  # 25 - inner
        # 5 - outerQuando a função interna faz a atribuição n = 25, uma nova variável n é criada, ocultando a variável n da função externa outer(). Como resultado, teremos dois valores diferentes sendo impressos.
Para indicar que a função interna deve usar a variável da função externa, utilizamos a palavra-chave nonlocal:
def outer():  # função externa
    n = 5
    def inner():  # função interna
        nonlocal n  # indica que n é a variável da função externa
        n = 25
        print(n)
    inner()  # 25
    print(n)
outer()  # 25Com o uso de nonlocal, garantimos que a variável n da função interna se refere à variável da função externa, permitindo que a função interna modifique seu valor.