Escopo de variáveis: local, global e nonlocal
1. Introdução ao conceito de escopo em Python
Escopo de variável define a região do código onde uma variável pode ser acessada. Em Python, o escopo determina quais nomes estão disponíveis em cada parte do programa, evitando conflitos e garantindo previsibilidade. Sem regras claras de escopo, variáveis poderiam ser alteradas acidentalmente em qualquer lugar, gerando bugs difíceis de rastrear.
Python segue a regra LEGB (Local, Enclosing, Global, Built-in) para resolver nomes em tempo de execução:
- Local — dentro da função atual
- Enclosing — funções externas aninhadas (closures)
- Global — nível do módulo
- Built-in — nomes embutidos como
print,len,range
Quando você usa uma variável, o Python percorre essa hierarquia até encontrar o nome ou lançar um NameError.
2. Escopo local: variáveis dentro de funções
Variáveis definidas dentro de uma função são locais — existem apenas durante a execução da função e são destruídas ao final.
def calcular_area(base, altura):
area = base * altura # 'area' é local à função
return area
resultado = calcular_area(5, 3)
print(resultado) # 15
# print(area) # NameError: name 'area' is not defined
Parâmetros de função também são variáveis locais:
def saudacao(nome):
mensagem = f"Olá, {nome}!" # 'nome' e 'mensagem' são locais
return mensagem
O ciclo de vida de uma variável local começa quando a função é chamada e termina quando ela retorna — cada chamada cria um novo escopo local independente.
3. Escopo global: variáveis no módulo
Variáveis definidas fora de qualquer função pertencem ao escopo global do módulo:
contador = 0 # variável global
def incrementar():
global contador # declara que vamos usar a variável global
contador += 1
incrementar()
incrementar()
print(contador) # 2
Sem a palavra-chave global, o Python criaria uma nova variável local contador dentro da função, ignorando a global. O uso de global deve ser cauteloso — alterar variáveis globais dentro de funções gera efeitos colaterais que prejudicam a legibilidade e a testabilidade do código.
# Exemplo problemático
total = 0
def adicionar(valor):
global total
total += valor # efeito colateral: modifica estado global
return total
# Melhor abordagem: receber e retornar valores
def adicionar_puro(total_atual, valor):
return total_atual + valor
4. A palavra-chave nonlocal e escopos aninhados
Funções definidas dentro de outras funções criam escopos aninhados. A palavra-chave nonlocal permite modificar variáveis do escopo intermediário (enclosing):
def fabricar_contador():
total = 0 # variável no escopo enclosing
def incrementar():
nonlocal total # modifica a variável do escopo externo
total += 1
return total
return incrementar
contador1 = fabricar_contador()
print(contador1()) # 1
print(contador1()) # 2
contador2 = fabricar_contador()
print(contador2()) # 1 (cada closure tem seu próprio 'total')
A diferença entre nonlocal e global:
globalrefere-se ao escopo do módulo (nível mais alto)nonlocalrefere-se ao escopo imediatamente externo (pode haver vários níveis de aninhamento)
def nivel_externo():
x = "externo"
def nivel_medio():
x = "médio" # sombreia o 'x' externo
def nivel_interno():
nonlocal x # modifica o 'x' do nível médio
x = "modificado"
nivel_interno()
print(x) # "modificado"
nivel_medio()
print(x) # "externo" (não foi modificado)
nivel_externo()
5. Sombreamento (shadowing) e conflitos de nomes
Sombreamento ocorre quando uma variável local tem o mesmo nome de uma variável global, "escondendo" a global:
mensagem = "Global"
def mostrar():
mensagem = "Local" # sombreia a variável global
print(mensagem) # "Local"
mostrar()
print(mensagem) # "Global" (a global não foi alterada)
Problemas comuns de shadowing:
def calcular_desconto(preco, desconto=0.1):
preco = preco * (1 - desconto) # 'preco' sombreia o parâmetro original
return preco
# Melhor prática: usar nomes distintos
def calcular_desconto(preco_original, desconto=0.1):
preco_final = preco_original * (1 - desconto)
return preco_final
6. Escopo em estruturas de controle (loops, condicionais)
Diferente de linguagens como C ou Java, Python não cria novo escopo em blocos if, for ou while. Variáveis definidas dentro dessas estruturas "vazam" para o escopo externo:
for i in range(5):
ultimo_valor = i
print(i) # 4 (vazou!)
print(ultimo_valor) # 4
# Em Python, 'i' continua existindo após o loop
Isso pode causar surpresas:
def encontrar_indice(lista, alvo):
for indice, valor in enumerate(lista):
if valor == alvo:
break
# 'indice' ainda existe aqui se o break foi executado
return indice # NameError se alvo não estiver na lista
# Solução: inicializar antes do loop
def encontrar_indice_seguro(lista, alvo):
indice = -1
for i, valor in enumerate(lista):
if valor == alvo:
indice = i
break
return indice
7. Built-in scope: o escopo embutido do Python
O escopo built-in contém funções e exceções padrão como print, len, int, ValueError. É o último nível na hierarquia LEGB:
# Python procura: local -> enclosing -> global -> built-in
print(len("teste")) # 'len' vem do escopo built-in
Sobrescrever nomes built-in acidentalmente é um erro comum:
# Cuidado! Sobrescreveu 'list' do built-in
list = [1, 2, 3]
print(list) # Funciona, mas...
# Agora não podemos mais usar list() como construtor
# nova_lista = list([4, 5, 6]) # TypeError: 'list' object is not callable
Para ver todos os nomes built-in:
print(dir(__builtins__))
8. Boas práticas e dicas finais
Prefira variáveis locais sempre que possível — elas tornam o código mais previsível, testável e reutilizável.
Evite global e nonlocal em código extenso — use parâmetros e retornos para passar dados entre funções.
Use closures com nonlocal para encapsulamento elegante:
def criar_cache():
cache = {} # encapsulado na closure
def obter(chave, funcao_calculo):
if chave not in cache:
cache[chave] = funcao_calculo()
return cache[chave]
return obter
cache_consulta = criar_cache()
resultado = cache_consulta("usuario_123", lambda: {"nome": "Maria"})
Nomeie variáveis com clareza para evitar shadowing acidental:
# Ruim
def processar(nome):
nome = nome.upper() # sombreia o parâmetro
# Bom
def processar(nome_original):
nome_processado = nome_original.upper()
Lembre-se: escopo não é apenas uma regra técnica — é uma ferramenta de design que ajuda a organizar o código e controlar o acesso aos dados. Use-a com intenção e seu código ficará mais limpo, seguro e fácil de manter.
Referências
- Documentação oficial: Escopo e namespaces em Python — Explicação detalhada da documentação oficial sobre como Python gerencia escopos e namespaces
- PEP 3104: Acessando nomes em escopos externos — Proposta que introduziu a palavra-chave
nonlocalno Python 3 - Real Python: Namespaces and Scope in Python — Tutorial completo com exemplos práticos sobre escopo, LEGB e closures
- GeeksforGeeks: Global, Local and Nonlocal variables in Python — Guia didático com exemplos comparativos entre os três tipos de escopo
- Python Morsels: Python's scoping rules — Artigo técnico explicando as regras de escopo com exemplos de shadowing e variáveis de loop