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.
- 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 padaria | No Python | Quem enxerga |
|---|---|---|
| O quadro da parede | Variável global: criada fora de qualquer função, no programa principal | Todo mundo: o programa principal e todas as funções |
| O rascunho de uma funcionária | Variá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:
- Local: as variáveis da própria função (e os parâmetros).
- A função de fora, quando há uma função dentro de outra (seção 6).
- Global: as variáveis do programa principal.
- 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:
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
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 ovisitasdo 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
visitasmexe 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
| Forma | Quando usar |
|---|---|
Parâmetro + return | Quase 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 |
global | Em 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
É 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().
É 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).
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.
É 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).
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ção | O 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 principal | Global: o programa e as funções podem ler |
| Função só lê um nome global | Funciona: lê a global |
| Função atribui a um nome | O nome é local na função inteira; a global de mesmo nome não muda |
| Função lê e atribui o mesmo nome | UnboundLocalError |
global nome | Dentro da função, nome passa a ser a global (pode ler e mudar) |
nonlocal nome | Numa função dentro de outra, nome passa a ser o da função de fora |
| Ordem de busca | Local → função de fora → global → embutidos (LEGB) |
| Mensagem | Causa 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 declaration | O global x veio depois de usar x |
TypeError: 'list' object is not callable | Uma 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:
pontos
pontos = pontos + 10
2. O valor de fora
Depois deste programa, quanto vale nome no programa principal?
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 nomedeixa a função mudar a global, mas o jeito preferido é parâmetro +return.nonlocalfaz o mesmo para a função de fora. A busca segue a ordem LEGB, e um nome seu pode esconder um embutido (list = …).