[python-brasil] Erro no python ao atribuir um valor

754 views
Skip to first unread message

Thomaz de Oliveira dos Reis

unread,
Mar 4, 2011, 4:01:15 PM3/4/11
to python...@yahoogrupos.com.br
Galera,

Esse código aqui da erro:
http://pastebin.com/uUTWG2wb

UnboundLocalError: local variable 'valor' referenced before assignment

Porém esses aqui não dão erro:
http://pastebin.com/GCr8s0Y2
http://pastebin.com/GFpqsLJa


Alguém saberia me explicar o porquê disso?


------------------------------------

Python-Brasil
http://www.python.org.br/wiki/AntesDePerguntar
Links do Yahoo! Grupos

<*> Para visitar o site do seu grupo na web, acesse:
http://br.groups.yahoo.com/group/python-brasil/

<*> Para sair deste grupo, envie um e-mail para:
python-brasi...@yahoogrupos.com.br

<*> O uso que você faz do Yahoo! Grupos está sujeito aos:
http://br.yahoo.com/info/utos.html


Felipe Zorzo

unread,
Mar 4, 2011, 7:08:20 PM3/4/11
to python...@yahoogrupos.com.br
Em 4 de março de 2011 18:01, Thomaz de Oliveira dos Reis
<tho...@gmail.com>escreveu:

> Galera,
>
> Esse código aqui da erro:
> http://pastebin.com/uUTWG2wb
>
> UnboundLocalError: local variable 'valor' referenced before assignment
>
> Porém esses aqui não dão erro:
> http://pastebin.com/GCr8s0Y2
> http://pastebin.com/GFpqsLJa
>
>
> Alguém saberia me explicar o porquê disso?
>
>
> ------------------------------------
>
> Python-Brasil
> http://www.python.org.br/wiki/AntesDePerguntar
> Links do Yahoo! Grupos
>
>
>

Quando você cria uma variável ela fica associada ao bloco em que ela foi
criada, nesse seu exemplo, a variável valor fica no escopo do
método metodo_1. Quando você faz o "valor = valor+1", na verdade você está
criando uma nova variável, com escopo mais interno (no método metodo_2). Aí
que está o problema.

A mesma coisa acontece nesse exemplo: http://dpaste.com/473746/

O Python permite que você acesse uma variável não-local, mas você não pode
atribuir valor a ela num escopo mais interno.

--
Felipe Bernardo Zorzo


[As partes desta mensagem que não continham texto foram removidas]

Felipe Bandeira

unread,
Mar 4, 2011, 6:32:51 PM3/4/11
to python...@yahoogrupos.com.br
Que eu lembre... você tem que inicializar a variável antes de utilizar :)

--
Felipe Bandeira


[As partes desta mensagem que não continham texto foram removidas]

------------------------------------

Caio Romão

unread,
Mar 4, 2011, 4:30:59 PM3/4/11
to python...@yahoogrupos.com.br, Thomaz de Oliveira dos Reis
2011/3/4 Thomaz de Oliveira dos Reis <tho...@gmail.com>:
<snip>

>
> Alguém saberia me explicar o porquê disso?
>

Isso é um problema de escopo. 'valor' está no escopo de metodo_1, mas
no momento que você faz assignment da variavel 'valor' dentro de
'metodo_2', o compilador (para bytecode) faz o que a gente conhece
como shadowing: se há um assignment para uma variável dentro de um
certo escopo, o python faz todas as referenciais àquele nome serem
locais.

Em python 3.x você pode dar uma "dica" ao compilador para não se
comportar assim adicionando a keyword nonlocal [1]

[1] http://docs.python.org/py3k/reference/simple_stmts.html#the-nonlocal-statement

Eric Lopes

unread,
Mar 4, 2011, 9:01:31 PM3/4/11
to python...@yahoogrupos.com.br, Caio Romão, Thomaz de Oliveira dos Reis
@ Thomaz:

o que você deveria estar querendo fazer provavelmente era verificar se
conseguiria alterar a variável do "metodo_1", isso não deu certo, o motivo
está na criação dinâmica de variáveis e variáveis com o mesmo nome, por trás
das câmeras o que acontece é o seguinte:
tudo é um objeto, certo?!, um objeto é formado por um identificador e uma
instancia de uma classe,
o identificador é o "nome" que vc dá pra ele, esse nome é um ponteiro para
uma instancia de uma classe (lembre-se que por trás ainda tem rotinas
escritas em c, ou java que utilizam explicitamente ou implicitamente
ponteiros e mallocs), mais ou menos assim:

nome_obj -> endereco_obj_na_memoria

quando vc cria um objeto o que o interpretador faz é nada mais nada menos
que criar um identificador, (no caso com nome "valor") que aponta para nulo

nome_obj -> Null

quando você atribui algo para essa nova variável que criou, o que ele faz é
o seguinte:
1º criar um espaço na memória com o tipo criado
2º fazer o identificador apontar para esse espaço na memória
assim vc fica com algo do tipo:
nome_obj -> novo_espaco_criado_do_tipo_escolhido

só que no python essas coisas ficam escondidas por trás do interpretador e
suas rotinas em c, vamos seguir os passos para rodar seu método:
>>print metodo_1(1)()
# o que acontece dentro do idle: blz, chama aí pra mim o "metodo_1" e passa
para ele o parâmetro 1

# agora dentro do método_1: êpa, recebi uma chamada, humm, o parâmetro é 1
# ok, interpretador, cria aí pra mim um identificador que aponta pra null
chamado valor,
# tá agora, cria um espaço na memória do tipo inteiro e atribui o valor 1
# certo, pra fechar faz o identificador "valor" apontar pra esse valor na
memória que eu criei aí.
# \0 ok, qual o próximo passo... criar uma função metodo_2 blz, cria ela pra
mim...
# próximo passo retornar pro idle essa metodo_2,
# ok, chama aí então a metodo_2...

#dentro da metodo_2: opa recebi uma chamada
# tenho que criar um identificador apontando pra null chamado valor
# agora criar um espaço na memoria do tipo... um null+integer ...ixi...,
valor não aponta pra ninguém
# retorne erro variável não inicializada

def metodo_1(valor):
def metodo_2():
valor = valor+1
return valor
return metodo_2

print metodo_1(1)()

entendeu.

nos dois exemplos que vc enviou, os nomes não são os mesmos, por isso não
existe esse problema, ele não atribui o identificador a ninguém igual e tudo
fica certinho

dentro do "metodo_2" o que acontece é o seguinte:
# cria aí o identificador valor2 apontando para null
# cria um espaço na memória do tipo daonde aponta o identificador
"valor":int + 1:int = int
# agora valor2 aponte para esse local criado na memória

def metodo_1(valor):
def metodo_2():
valor2 = valor+1
return valor2
return metodo_2

print metodo_1(1)()

def metodo_1(valor):
def metodo_2():
valor = 1
return valor
return metodo_2

print metodo_1(1)()

Em 4 de março de 2011 18:30, Caio Romão <caio...@gmail.com> escreveu:

>
>
> 2011/3/4 Thomaz de Oliveira dos Reis <tho...@gmail.com>:
> <snip>
>
> >
> > Alguém saberia me explicar o porquê disso?
> >
>
> Isso é um problema de escopo. 'valor' está no escopo de metodo_1, mas
> no momento que você faz assignment da variavel 'valor' dentro de
> 'metodo_2', o compilador (para bytecode) faz o que a gente conhece
> como shadowing: se há um assignment para uma variável dentro de um
> certo escopo, o python faz todas as referenciais àquele nome serem
> locais.
>
> Em python 3.x você pode dar uma "dica" ao compilador para não se
> comportar assim adicionando a keyword nonlocal [1]
>
> [1]
> http://docs.python.org/py3k/reference/simple_stmts.html#the-nonlocal-statement
>
>
>

--
"The real voyage of discovery consists not in seeking new landscapes but in
having new eyes."
-Marcel Proust


[As partes desta mensagem que não continham texto foram removidas]

------------------------------------

Eric Lopes

unread,
Mar 4, 2011, 9:06:27 PM3/4/11
to python...@yahoogrupos.com.br
epa, esqueci de comentar, no ultimo exemplo é a mesma coisa:

ele "recria" o "valor" mas não dá erro por atribuir um inteiro logo de cara

def metodo_1(valor):
def metodo_2():
valor = 1
return valor
return metodo_2

é isso aí, espero ter ajudado,
abç.

Thomaz de Oliveira dos Reis

unread,
Mar 4, 2011, 9:13:57 PM3/4/11
to python...@yahoogrupos.com.br
eu acho curioso, pq quando vc faz:

valor = 1
valor = valor + 1
print valor

você obtem 2, logo ele primeiro faz o valor+1 pra depois atribuir a
valor o valor... não seria?

2011/3/4 Eric Lopes <contato.e...@gmail.com>:

Felipe Zorzo

unread,
Mar 4, 2011, 9:15:34 PM3/4/11
to python...@yahoogrupos.com.br
Em 4 de março de 2011 23:13, Thomaz de Oliveira dos Reis
<tho...@gmail.com>escreveu:

> eu acho curioso, pq quando vc faz:
>
> valor = 1
> valor = valor + 1
> print valor
>
> você obtem 2, logo ele primeiro faz o valor+1 pra depois atribuir a
> valor o valor... não seria?


Não quando a soma está em um escopo mais interno (por isso ele lança um
UnboundLocalError).

--
Felipe Bernardo Zorzo


[As partes desta mensagem que não continham texto foram removidas]

------------------------------------

Python-Brasil
http://www.python.org.br/wiki/AntesDePerguntar
Links do Yahoo! Grupos

<*> Para visitar o site do seu grupo na web, acesse:

Elias de Oliveira

unread,
Mar 4, 2011, 9:17:40 PM3/4/11
to python...@yahoogrupos.com.br
Em 4 de março de 2011 23:13, Thomaz de Oliveira dos Reis
<tho...@gmail.com> escreveu:

> eu acho curioso, pq quando vc faz:
>
> valor = 1
> valor = valor + 1
> print valor
>
> você obtem 2, logo ele primeiro faz o valor+1 pra depois atribuir a
> valor o valor... não seria?

Mas neste caso, a variavel valor já foi iniciada, no exemplo que vc
mostrou existem duas variaveis valor, pois elas são locais. Uma não
sabe da existencia da outra. Apesar deste material ser implementado em
C, acho interessante[1], pois o funcionamento das variaveis neste caso
é semelhante;

[1] - http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c760.html


--
Elias Granja
Campinas - SP

Eric Lopes

unread,
Mar 4, 2011, 10:49:35 PM3/4/11
to python...@yahoogrupos.com.br, Elias de Oliveira
eu fui jantar e fiquei pensando nisso Thomaz, o primeiro contra - exemplo
que me veio a cabeça foi justamente algo parecido com o que vc colocou.

eu não saberia responder isso com certeza, por sorte o Elias e o Felipe
estão aí.
mas com certeza foi um estudo de caso interessante, eu cavuquei um pouquinho
mais e pelo que eu entendi, ele deve fazer uma verificação das variáveis
utilizadas na função antes de criar a função em si com as rotinas em c e
reserva o identificador delas antes de começar a fazer atribuições, eu fiz
três testes um pouco mais simples do que os seus, em vez de retornar o
endereço de funções eu trabalhei só em cima da variável "valor" e retornei
somente o tipo da variavel "valor":

#1 - vimos que o resultado é 1 3 1, ou seja, "valor" de metodo_1 não é
alterado, sobrescrito, etc
def metodo_1(valor):
print valor
def metodo_2():
valor = 3
return valor
print metodo_2()
return valor
print metodo_1(1)

#2 - nesse exemplo vê-se que metodo_2 tem acesso a "valor" pois metodo_1
está em um nível acima
def metodo_1(valor):
print valor
def metodo_2():
print valor
valor2 = 3
return valor
print metodo_2()
return valor
print metodo_1(1)

# 3 - aqui que ferra tudo, metodo_2 não pode alterar "valor" da camada
superior (bendito seja o encapsulamento)
# então ele entende que vai ter que criar essa variável dentro de metodo_2,
mas ao fazer isso reserva o
# identificador para essa variável local e ferra-se tudo, note que pelo
simples fato de ter uma atribuição a esse
# identificador, daí (acho) que ele reserva o nome para uso interno,
atribuindo null, como expliquei no e-mail
# anterior e finalmente o inocente "print valor" que havia funcionado no
exemplo 2 não funciona mais, pois
# agora "valor" não está apontando pra lugar nenhum

def metodo_1(valor):
print valor
def metodo_2():
print valor # erro: valor não inicializado
valor = 3
return valor
print metodo_2()
return valor
print metodo_1(1)

muito legal o estudo de caso que você levantou, eu passaria despercebido por
isso facilmente
abç

Em 4 de março de 2011 23:17, Elias de Oliveira
<con...@eliasgranja.com>escreveu:

>
>
> Em 4 de março de 2011 23:13, Thomaz de Oliveira dos Reis
> <tho...@gmail.com> escreveu:
>
>
> > eu acho curioso, pq quando vc faz:
> >
> > valor = 1
> > valor = valor + 1
> > print valor
> >
> > você obtem 2, logo ele primeiro faz o valor+1 pra depois atribuir a
> > valor o valor... não seria?
>
> Mas neste caso, a variavel valor já foi iniciada, no exemplo que vc
> mostrou existem duas variaveis valor, pois elas são locais. Uma não
> sabe da existencia da outra. Apesar deste material ser implementado em
> C, acho interessante[1], pois o funcionamento das variaveis neste caso
> é semelhante;
>
> [1] - http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c760.html
>
> --
> Elias Granja
> Campinas - SP
>
>
>

--

"The real voyage of discovery consists not in seeking new landscapes but in
having new eyes."
-Marcel Proust


[As partes desta mensagem que não continham texto foram removidas]

------------------------------------

Thomaz de Oliveira dos Reis

unread,
Mar 9, 2011, 11:03:24 AM3/9/11
to python...@yahoogrupos.com.br
Interessante.

Curioso que ele da erro na linha do print, mas o erro não acontece se
trocarmos na linha de baixo a atribuição.

É como se antes de executar o método o interpretador procurasse por
todas as atribuições de variáveis.


2011/3/5 Eric Lopes <contato.e...@gmail.com>:

'

Luciano Ramalho

unread,
Mar 13, 2011, 9:30:22 PM3/13/11
to python...@yahoogrupos.com.br, Thomaz de Oliveira dos Reis
2011/3/9 Thomaz de Oliveira dos Reis <tho...@gmail.com>:

> Interessante.
>
> Curioso que ele da erro na linha do print, mas o erro não acontece se
> trocarmos na linha de baixo a atribuição.
>
> É como se antes de executar o método o interpretador procurasse por
> todas as atribuições de variáveis.

De fato ele faz isso no contexto de funções: ele dá uma olhada adiante
e assume que qualquer variável que sofra uma atribuição é uma variável
local (exceto aquelas explicitamente declaradas como global (ou
nonlocal no Python 3)).

Que eu saiba, é a única exceção para a regra de que o Python só lê o
código-fonte uma vez, e de cima para baixo.

--
Luciano Ramalho
programador repentista || stand-up programmer
Twitter: @luciano

Leonardo Santagada

unread,
Mar 13, 2011, 10:46:13 PM3/13/11
to python...@yahoogrupos.com.br
2011/3/13 Luciano Ramalho <ram...@gmail.com>:

> Que eu saiba, é a única exceção para a regra de que o Python só lê o
> código-fonte uma vez, e de cima para baixo.
>

Eu gosto mais da analogia de que ele le o código uma vez, e depois
executa de cima pra baixo (até pq não é uma analogia é exatamente como
ele faz :D) Essa primeira leitura serve para várias coisas, não só
para decidir o escopo das variaveis como também para erros de encoding
no codigo fonte, otimizações, e algumas outras decisões

--
Leonardo Santagada

Luciano Ramalho

unread,
Mar 13, 2011, 10:59:02 PM3/13/11
to python...@yahoogrupos.com.br
2011/3/13 Leonardo Santagada <sant...@gmail.com>:

> 2011/3/13 Luciano Ramalho <ram...@gmail.com>:
>> Que eu saiba, é a única exceção para a regra de que o Python só lê o
>> código-fonte uma vez, e de cima para baixo.
>>
>
> Eu gosto mais da analogia de que ele le o código uma vez, e depois
> executa de cima pra baixo (até pq não é uma analogia é exatamente como
> ele faz :D) Essa primeira leitura serve para várias coisas, não só
> para decidir o escopo das variaveis como também para erros de encoding
> no codigo fonte, otimizações, e algumas outras decisões

Legal. Podemos dizer então que ele lê o código-fonte uma vez, daí gera
o bytecode e executa o bytecode, confere?

Para quem se interessa por este tema de como o interpretador realmente
funciona, eu bolei um exercício legal que uso em minhas oficinas.

O exercício é o seguinte: leia o código-fonte [1], mas não o execute
no Python. Escreva num papel em que ordem os pontos indicados por ->
vão aparecer na saída do programa.

Dica: o primeiro ponto que aparece na saída é '-> 1', mas o segundo
não é '-> 2'.

[1] http://code.google.com/p/propython/source/browse/fundamentos/execucao_pergunta.py

--
Luciano Ramalho
programador repentista || stand-up programmer
Twitter: @luciano

Reply all
Reply to author
Forward
0 new messages