Python Escopo (global e local)

Nas aulas de funções, apareceu várias vezes um NameError estranho: a variável existia dentro da função, mas, fora dela, o Python dizia que ela "não está definida". Não é defeito: cada variável tem um lugar onde ela existe, e esse lugar se chama escopo (em inglês, scope, "alcance"). Nesta aula você vai entender onde cada variável vive, quem enxerga quem, e o erro mais confuso desse assunto, o UnboundLocalError.

Você vai aprender
  • O que é uma variável local (de dentro da função) e uma global (do programa principal).
  • Que a função enxerga as globais, mas o programa principal não enxerga as locais.
  • O que acontece quando dentro e fora há uma variável com o mesmo nome.
  • Por que aparece o UnboundLocalError, com a mensagem real.
  • A palavra global, e por que o jeito melhor quase sempre é parâmetro + return.
  • O nonlocal, e o que acontece quando uma variável toma o nome de uma função embutida.

A ideia: o quadro da parede e o rascunho de cada uma

Na parede da padaria há um quadro com os avisos de todo mundo: "taxa de entrega: R$ 5". Qualquer funcionária que passa consegue ler. Já cada uma, quando faz uma conta, usa um papel de rascunho próprio. Ela anota, calcula e, quando acaba a tarefa, joga o rascunho fora. Ninguém mais vê o que estava escrito ali.

Na padariaNo PythonQuem enxerga
O quadro da paredeVariável global: criada fora de qualquer função, no programa principalTodo mundo: o programa principal e todas as funções
O rascunho de uma funcionáriaVariável local: criada dentro de uma função (inclusive os parâmetros)Só aquela função, e só enquanto ela está rodando

1. A variável local: só existe dentro da função

def fechar_caixa():
    total = 150 + 230
    print(f"Dentro da função: {total}")


fechar_caixa()
print(f"Fora da função: {total}")
$ python3 local.py
Dentro da função: 380
Traceback (most recent call last):
  File "/home/ana/local.py", line 7, in <module>
    print(f"Fora da função: {total}")
                             ^^^^^
NameError: name 'total' is not defined

A variável total foi criada dentro da função: é local, o rascunho da fechar_caixa. Dentro dela, funcionou. Quando a função terminou, o rascunho foi para o lixo. Na linha 7, já no programa principal (<module>), o nome total não existe mais. Os parâmetros funcionam igual: são variáveis locais que já nascem preenchidas.

Para levar um valor de dentro para fora, a função tem de devolvê-lo, com return (aula return). É a funcionária entregando o resultado antes de jogar o rascunho fora.

Cada chamada, um rascunho novo

As variáveis locais não sobrevivem de uma chamada para a outra. Cada chamada começa do zero:

def contar_visita():
    visitas = 0
    visitas = visitas + 1
    print(f"Visitas: {visitas}")


contar_visita()
contar_visita()
contar_visita()
$ python3 cada_chamada.py
Visitas: 1
Visitas: 1
Visitas: 1

A Ana queria ver 1, 2, 3, mas a cada chamada a função cria um visitas novo, valendo 0. A seção 4 mostra como resolver.

Missão: o troco é calculado dentro da função, mas o print de fora não o encontra. Rode, veja o NameError, e faça a função devolver o troco para o programa principal.

def calcular_troco(pago, conta):
    troco = pago - conta


calcular_troco(50, 32.5)
print(f"Troco: R$ {troco:.2f}")
def calcular_troco(pago, conta):
    troco = pago - conta
    return troco


troco = calcular_troco(50, 32.5)
print(f"Troco: R$ {troco:.2f}")

2. A variável global: a função consegue ler

O caminho contrário funciona: uma variável criada no programa principal pode ser lida dentro da função.

taxa_entrega = 5


def preco_final(preco):
    return preco + taxa_entrega


print(preco_final(20))
print(preco_final(42))
$ python3 global_ler.py
25
47

Dentro de preco_final, o Python procura taxa_entrega entre as variáveis locais e não acha. Então ele procura no programa principal, e acha: é o quadro da parede. A busca segue esta ordem:

  1. Local: as variáveis da própria função (e os parâmetros).
  2. A função de fora, quando há uma função dentro de outra (seção 6).
  3. Global: as variáveis do programa principal.
  4. Embutidos: os nomes que já vêm com o Python, como print, len, list.

O primeiro lugar onde o nome aparece ganha. (Os programadores chamam essa ordem de LEGB: Local, Enclosing, Global, Built-in.)


3. O mesmo nome dentro e fora

E se a função atribuir um valor a um nome que também existe fora?

nome = "Ana"


def trocar():
    nome = "Bruno"
    print(f"Dentro: {nome}")


trocar()
print(f"Fora: {nome}")
$ python3 mesmo_nome.py
Dentro: Bruno
Fora: Ana

O global não mudou. O nome = "Bruno" de dentro da função criou uma variável local nova, que por acaso tem o mesmo nome. São duas variáveis diferentes: a funcionária escreveu "Bruno" no rascunho dela; o quadro da parede continua dizendo "Ana". A regra:

A regra que explica quase tudo

Se uma função atribui um valor a um nome (nome = …, nome += …) em qualquer lugar do corpo, esse nome é local em toda a função, do começo ao fim. Se ela só lê o nome, ele pode ser global.

Com parâmetros acontece o mesmo: mudar o parâmetro não muda a variável de fora.

def aumentar(preco):
    preco = preco + 1
    print(f"Dentro: {preco}")


preco = 10
aumentar(preco)
print(f"Fora: {preco}")
$ python3 parametro_local.py
Dentro: 11
Fora: 10
Mas a lista muda!

Com listas e dicionários há uma diferença importante entre trocar o valor da variável (x = …, que cria uma local) e mexer dentro do valor (x.append(…)). A função recebe a mesma lista que está fora, não uma cópia; então, se ela mexer dentro, a mudança aparece fora:

def adicionar_cafe(compras):
    compras.append("café")


lista = ["arroz", "feijão"]
adicionar_cafe(lista)
print(lista)
$ python3 lista_muda.py
['arroz', 'feijão', 'café']

Isso não é escopo, é a diferença entre valores que mudam por dentro e valores que não mudam. Tem aula própria: Mutável × Imutável.


4. O UnboundLocalError

A Ana quer contar as visitas ao site da padaria. Ela tirou o visitas = 0 de dentro da função (para não zerar a cada chamada) e o colocou fora:

visitas = 0


def contar_visita():
    visitas = visitas + 1
    print(f"Visitas: {visitas}")


contar_visita()
$ python3 unbound.py
Traceback (most recent call last):
  File "/home/ana/unbound.py", line 9, in <module>
    contar_visita()
    ~~~~~~~~~~~~~^^
  File "/home/ana/unbound.py", line 5, in contar_visita
    visitas = visitas + 1
              ^^^^^^^
UnboundLocalError: cannot access local variable 'visitas' where it is not associated with a value

Linha por linha:

File "…", line 9, in <module>
O programa principal, na linha 9, chamou contar_visita().
File "…", line 5, in contar_visita
Dentro da função, na linha 5, deu o erro. O ^^^^^^^ aponta o visitas do lado direito do =, o que está sendo lido.
UnboundLocalError
"Erro de variável local não ligada": uma variável local foi usada antes de receber um valor.
cannot access local variable 'visitas' where it is not associated with a value
"Não dá para acessar a variável local 'visitas', que ainda não tem valor".

Mas visitas não era global, valendo 0? Lembre a regra da seção 3: a linha visitas = visitas + 1 atribui a visitas. Então, para o Python, visitas é local na função inteira, e ele decide isso antes de rodar a primeira linha do corpo. Na hora de calcular visitas + 1, ele vai buscar o visitas local, que ainda está vazio, e nem olha o global.

Basta a atribuição aparecer em qualquer lugar da função, até depois da leitura:

preco = 10


def mostrar():
    print(preco)
    preco = 12


mostrar()
$ python3 unbound_print.py
Traceback (most recent call last):
  File "/home/ana/unbound_print.py", line 9, in <module>
    mostrar()
    ~~~~~~~^^
  File "/home/ana/unbound_print.py", line 5, in mostrar
    print(preco)
          ^^^^^
UnboundLocalError: cannot access local variable 'preco' where it is not associated with a value

Sem a linha 6, o print(preco) mostraria o 10 do global, como na seção 2. Com ela, preco virou local na função toda, e a linha 5 tenta ler uma local que ainda não tem valor.


5. Dois jeitos de resolver: global, ou parâmetro + return

Com a palavra global

A palavra global diz ao Python: "dentro desta função, este nome é o do quadro da parede, e não um rascunho":

visitas = 0


def contar_visita():
    global visitas
    visitas = visitas + 1
    print(f"Visitas: {visitas}")


contar_visita()
contar_visita()
contar_visita()
print(f"No fim: {visitas}")
$ python3 com_global.py
Visitas: 1
Visitas: 2
Visitas: 3
No fim: 3
global visitas
Vem no começo do corpo, antes de qualquer uso. A partir daí, ler e atribuir visitas mexe na variável do programa principal.

Com parâmetro e return (o preferido)

O mesmo resultado, sem global: o valor entra pelo parâmetro e sai pelo return.

def contar_visita(visitas):
    visitas = visitas + 1
    print(f"Visitas: {visitas}")
    return visitas


visitas = 0
visitas = contar_visita(visitas)
visitas = contar_visita(visitas)
visitas = contar_visita(visitas)
print(f"No fim: {visitas}")
$ python3 sem_global.py
Visitas: 1
Visitas: 2
Visitas: 3
No fim: 3
FormaQuando usar
Parâmetro + returnQuase sempre. Lendo só a chamada, visitas = contar_visita(visitas), você sabe o que entra e o que sai. A função funciona sozinha, fácil de testar e de reaproveitar
globalEm programas bem pequenos, ou para uma configuração do programa inteiro. Com muitas funções mexendo nas mesmas globais, fica difícil descobrir quem mudou o quê
Ler uma global sem mudar (seção 2)Tudo bem para valores fixos, como TAXA_ENTREGA = 5. O costume é escrever esses nomes em MAIÚSCULAS, para avisar que não mudam

6. Função dentro de função: nonlocal

Dá para definir uma função dentro de outra. A de dentro enxerga as variáveis da de fora (o "E" do LEGB). Para mudá-las, existe o nonlocal, que funciona como o global, só que aponta para a função de fora:

def padaria():
    pedidos = 0

    def novo_pedido():
        nonlocal pedidos
        pedidos = pedidos + 1

    novo_pedido()
    novo_pedido()
    print(f"Pedidos: {pedidos}")


padaria()
$ python3 nonlocal.py
Pedidos: 2

Por enquanto, só guarde que ele existe. Funções dentro de funções aparecem de verdade na aula Decoradores.


Quando dá errado

Usar fora a variável de dentro

É o NameError: name 'total' is not defined da seção 1. Como consertar: devolva o valor com return e guarde-o numa variável no programa principal: total = fechar_caixa().

Mudar uma global sem avisar

É o UnboundLocalError: cannot access local variable '…' where it is not associated with a value da seção 4. Aparece quando a função lê um nome e também atribui a ele. Como consertar: passe o valor por parâmetro e devolva com return (ou, em último caso, use global).

O global depois do uso
visitas = 0


def contar_visita():
    visitas = 1
    global visitas
$ python3 global_tarde.py
  File "/home/ana/global_tarde.py", line 6
    global visitas
    ^^^^^^^^^^^^^^
SyntaxError: name 'visitas' is assigned to before global declaration

"O nome 'visitas' recebe um valor antes da declaração global". A declaração tem de vir antes de qualquer uso do nome na função. Como consertar: coloque o global visitas na primeira linha do corpo.

"Achei que tinha mudado": o mesmo nome dentro e fora

É a seção 3: uma atribuição dentro da função cria uma local, e a global continua como estava, sem erro nenhum. Se uma variável "não muda" depois de chamar a função, é provável que a função tenha mudado só uma local com o mesmo nome. Como consertar: devolva o valor novo com return e atribua fora: preco = aumentar(preco).

Uma variável com o nome de uma função embutida

Os nomes embutidos (o "B" do LEGB) ficam em último lugar na busca. Se você cria uma variável com o mesmo nome, ela ganha, e a função embutida some do seu programa:

list = ["arroz", "feijão"]
print(list)
letras = list("café")
$ python3 embutida.py
['arroz', 'feijão']
Traceback (most recent call last):
  File "/home/ana/embutida.py", line 3, in <module>
    letras = list("café")
             ~~~~^^^^^^^^
TypeError: 'list' object is not callable

"Um objeto do tipo lista não pode ser chamado". Na linha 3, list já não é a função que cria listas: é a variável da linha 1, que é uma lista, e uma lista não se chama com parênteses. O mesmo acontece com sum, max, input, print, str… Como consertar: escolha outro nome (compras, lista_compras).


Referência rápida

SituaçãoO que acontece
Variável criada dentro da função (ou parâmetro)Local: só existe dentro, durante a chamada. Cada chamada começa do zero
Variável criada no programa principalGlobal: o programa e as funções podem ler
Função só lê um nome globalFunciona: lê a global
Função atribui a um nomeO nome é local na função inteira; a global de mesmo nome não muda
Função lê e atribui o mesmo nomeUnboundLocalError
global nomeDentro da função, nome passa a ser a global (pode ler e mudar)
nonlocal nomeNuma função dentro de outra, nome passa a ser o da função de fora
Ordem de buscaLocal → função de fora → global → embutidos (LEGB)
MensagemCausa provável
NameError: name 'x' is not defined (fora da função)x é local de uma função: use return
UnboundLocalError: cannot access local variable 'x' …A função lê e atribui x: use parâmetro + return, ou global x
SyntaxError: name 'x' is assigned to before global declarationO global x veio depois de usar x
TypeError: 'list' object is not callableUma variável tomou o nome da função embutida list

Exercícios

1. A palavra que avisa

Complete para que a função mude a variável pontos do programa principal:

def ganhar():
     pontos
    pontos = pontos + 10

2. O valor de fora

Depois deste programa, quanto vale nome no programa principal?

nome = "Carla"
def mudar():
    nome = "Davi"
mudar()
print(nome)  # mostra:

3. Uma variável criada dentro de uma função…

4. Por que total = total + 1 dentro de uma função dá UnboundLocalError, mesmo existindo um total global?

5. Qual é o jeito preferido de uma função mudar um valor do programa principal?

Missão final: o estoque da padaria

Missão: a função vender deveria tirar unidades do estoque, mas dá UnboundLocalError. Conserte, de preferência com parâmetro e return (ou com global). O estoque começa em 20 e as vendas são de 3 e de 5.

estoque = 20


def vender(quantidade):
    estoque = estoque - quantidade
    print(f"Vendeu {quantidade}. Restam {estoque}.")


vender(3)
vender(5)
def vender(estoque, quantidade):
    estoque = estoque - quantidade
    print(f"Vendeu {quantidade}. Restam {estoque}.")
    return estoque


estoque = 20
estoque = vender(estoque, 3)
estoque = vender(estoque, 5)

Resumo

  • Escopo é o lugar onde uma variável existe.
  • Local: criada dentro da função (parâmetros também). Só existe ali, durante a chamada; fora dela, NameError.
  • Global: criada no programa principal. As funções podem ler.
  • Se a função atribui a um nome, ele é local na função inteira. Ler antes de atribuir dá UnboundLocalError.
  • global nome deixa a função mudar a global, mas o jeito preferido é parâmetro + return.
  • nonlocal faz o mesmo para a função de fora. A busca segue a ordem LEGB, e um nome seu pode esconder um embutido (list = …).