Pegadinha com funções e variáveis globais

Diga aí, o que você acha que o código abaixo irá imprimir na tela?

def func():
    print x

x = 42
func()

A resposta é óbvia: 42. OK, sem pegadinhas por enquanto. E o código abaixo, o que irá imprimir?

def func():
    print x
    x = 1

x = 42
func()

Pelo que sabemos até então, somos levados a crer que o código acima irá imprimir 42 na tela, correto? Mas, veja a saída que recebemos na cabeça ao executar o código:

Traceback (most recent call last):
File "<pyshell#3>", line 6, in 
    func()
File "<pyshell#3>", line 2, in func
    print x
UnboundLocalError: local variable 'x' referenced before assignment

wat

UnboundLocalError significa que estamos acessando uma variável antes dela ter sido criada no escopo da função func(). A linha que está gerando o erro é a linha 2, que contém print x. Entretanto, observe com cuidado os dois exemplos de código acima. No primeiro deles também estamos fazendo print x na primeira linha da função. Inclusive, o valor impresso pela função foi o valor da variável x definida no escopo global.

A culpada por esse erro é a linha 3 (x = 1) e a explicação é simples: o código da função é analisado e x é definido estaticamente pelo compilador como uma variável (ou melhor, como um nome) local ao escopo da função quando este encontra a atribuição x = 1. E isso vale para a função inteira, mesmo que a atribuição ocorra somente na última linha dela. Essa análise é feita estaticamente durante a criação da função func, quando o compilador encontra a declaração de criação de função def. Assim, mais tarde quando func() é chamada, print x tenta imprimir o valor de uma variável local antes dela ter algum valor atribuído a si.

Existe uma regra bem simples:

Sempre que o compilador encontra uma atribuição a um nome dentro de uma função, ele assume que tal nome é local em toda a função, não importando onde a atribuição ocorre.

O código a seguir funciona

def func():
    x = 1
    print x

x = 42
func()

mas imprime 1, pois ao encontrar x = 1, o compilador assume que x é uma variável local. Mas e se precisarmos alterar o valor do x global dentro da função? Antes de qualquer coisa, vamos ver se o x global (de valor 42, inicialmente) é alterado:


def func():
    x = 1
    print x

x = 42
func()
print x

O programa acima irá imprimir:

1
42

Ou seja, o x global não foi alterado. Se quiser mesmo alterar o valor de uma variável global dentro de uma função, Python exige que identifiquemos a mesma como global dentro da função. Isso pode ser feito com a estranhíssima construção global:


def func():
    global x
    x = 1
    print x

x = 42
func()
print x

O código acima imprime:

1
1

Ou seja, a global x foi alterada pela atribuição feita dentro de func().

ALERTA: criar variáveis globais e alterar o valor delas dentro de funções é uma péssima prática.  O código fica mais difícil de ler e modificar (você precisa saber todos os lugares onde a variável global está sendo usada) e pode levar a bugs bem difíceis de serem encontrados. Não é por menos que Python força o programador a explicitar a referência a uma variável global quando quiser alterá-la fora do escopo global. Soa quase como uma autodeclaração do programador: “sim, eu sou tosco e estou prestes a fazer mer** no meu código”. 😀

Anúncios

6 comentários sobre “Pegadinha com funções e variáveis globais

  1. Não entendi porque é uma péssima prática utilizar as globais dentro das funções. E aliás, mais uma vez, parabéns pelo blog é realmente muito esclarecedor. Abraço.

  2. Gabriel, se você começar a alterar valores de variáveis globais dentro de funções, seu código vai ficar bem difícil de entender e manter. Assim, a execução de suas funções passam a ter efeitos colaterais que não ficam mais restritos aos parâmetros/valores de retorno, mas sim a todo o código.

  3. Com relação ao seu comentário anterior; isso é válido apenas para python ou também serve para outras linguagens?!
    Gostaria de entender melhor sobre isso!

    • Boas práticas de programação devem ser levadas em conta por seus paradigmas, e para POO, quanto mais independente e menos acoplado estiver o seu código, mais vantagens você será capaz de extrair do paradigma. Evitando problemas e ampliando a proficiência, como já previsto por tal filosofia de desenvolvimento.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s