Python globals, locals, e UnboundLocalError
-
03-07-2019 - |
Pergunta
Eu corri através deste caso de UnboundLocalError
recentemente, o que parece estranho:
import pprint
def main():
if 'pprint' in globals(): print 'pprint is in globals()'
pprint.pprint('Spam')
from pprint import pprint
pprint('Eggs')
if __name__ == '__main__': main()
Que produz:
pprint is in globals()
Traceback (most recent call last):
File "weird.py", line 9, in <module>
if __name__ == '__main__': main()
File "weird.py", line 5, in main
pprint.pprint('Spam')
UnboundLocalError: local variable 'pprint' referenced before assignment
pprint
está claramente ligado no globals
, e vai ser ligado no locals
na seguinte declaração. Alguém pode oferecer uma explicação de por que ele não está feliz resolver pprint
ao obrigatório em globals
aqui?
Editar: Graças às boas respostas que eu possa esclarecer a minha pergunta com a terminologia relevante:
Em tempo de compilação do pprint
identificador é marcado como local para o quadro. Será que o modelo de execução não têm nenhuma distinção , onde dentro do quadro do identificador local é obrigado? ele pode dizer, "referem-se ao global vinculativo até esta instrução bytecode, altura em que foi retomada a uma ligação local", ou faz o modelo de execução não são responsáveis ??por isso?
Solução
Looks como Python vê a linha from pprint import pprint
e marcas pprint
como um nome local para main()
antes executar qualquer código. Desde Python pensa pprint deveria ser uma variável local, referenciando-o com pprint.pprint()
antes "atribuir" com a declaração from..import
, ele lança esse erro.
Isso é tanto sentido como eu posso fazer isso.
A moral, é claro, é sempre colocar essas declarações import
no topo do escopo.
Outras dicas
Onde está a surpresa? Qualquer variável global para um escopo que você atribuir novamente nesse âmbito é marcada local para que alcance pelo compilador.
Se as importações seriam tratados de forma diferente, que seria surpreendente imho.
Pode fazer um caso de não nomear módulos após símbolos usados ??no mesmo, ou vice-versa, no entanto.
Bem, isso foi o suficiente interessante para mim experimentar um pouco e eu ler http: / /docs.python.org/reference/executionmodel.html
Em seguida, fez alguns ajustes com o seu código aqui e ali, isso é o que eu poderia encontrar:
código:
import pprint
def two():
from pprint import pprint
print globals()['pprint']
pprint('Eggs')
print globals()['pprint']
def main():
if 'pprint' in globals():
print 'pprint is in globals()'
global pprint
print globals()['pprint']
pprint.pprint('Spam')
from pprint import pprint
print globals()['pprint']
pprint('Eggs')
def three():
print globals()['pprint']
pprint.pprint('Spam')
if __name__ == '__main__':
two()
print('\n')
three()
print('\n')
main()
saída:
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Eggs'
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
pprint is in globals()
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
<function pprint at 0xb7d596f4>
'Eggs'
No two()
método from pprint import pprint
mas não se sobrepõe à pprint
nome em globals
, desde o global
palavra-chave é não usado no âmbito da two()
.
Em three()
método, pois não há declaração de nome pprint
em âmbito local o padrão é o pprint
nome global que é um módulo
Considerando que, main()
, em primeiro lugar a palavra-chave global
é usado para que todas as referências a pprint
no âmbito da main()
método irá se referir ao nome global
pprint
. Que como podemos ver é um módulo em primeiro lugar e é anulado na global
namespace
com um método de como fazemos o from pprint import pprint
Embora este não pode estar respondendo a pergunta como tal, mas, no entanto, a sua algum fato interessante que eu penso.
=====================
Editar Outra coisa interessante.
Se você tem uma palavra a dizer módulo:
mod1
from datetime import datetime
def foo():
print "bar"
e outro digamos método:
mod2
import datetime
from mod1 import *
if __name__ == '__main__':
print datetime.datetime.now()
que à primeira vista é aparentemente correta desde que você tenha importado o datetime
módulo em mod2
.
Agora, se você tentar executar mod2 como um script que irá lançar um erro:
Traceback (most recent call last):
File "mod2.py", line 5, in <module>
print datetime.datetime.now()
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
porque o segundo from mod2 import *
importação tem anulado o datetime
nome no namespace, portanto, o primeiro import datetime
não é mais válido.
Moral:. Assim, a ordem das importações, a natureza das importações (de x import *) ea consciência das importações dentro de módulos importados - importa ??strong>
Esta questão foi respondida há várias semanas, mas eu acho que pode esclarecer as respostas um pouco. Primeiro alguns fatos.
1: Em Python,
import foo
é quase exatamente o mesmo que
foo = __import__("foo", globals(), locals(), [], -1)
2:. Ao executar código em uma função, se Python encontra uma variável que não tenha sido definido na função, no entanto, parece no escopo global
3: Python tem uma otimização ele usa para funções chamadas "locais". Quando Python tokenizes uma função, ele mantém o controle de todas as variáveis ??você atribui a. Ele atribui a cada uma dessas variáveis ??um número a partir de um número inteiro monotonicamente crescente local. Quando Python executa a função, ele cria um array com o maior número de aberturas, como existem variáveis ??locais, e atribui a cada slot de um valor especial que significa "não tenha sido atribuído a ainda", e é aí que os valores para as variáveis ??são armazenadas. Se você faz referência a um local que não tenha sido atribuído a ainda, Python vê esse valor especial e lança uma exceção UnboundLocalValue.
O palco já está definido. Seu "de pprint importação pprint" é realmente uma forma de atribuição. Então Python cria uma variável local chamada "pprint", que obstrui a variável global. Então, quando você se referir a "pprint.pprint" na função, você acertar o valor especial e Python lança a exceção. Se você não tem essa instrução de importação na função, Python usaria o primeiro e depois olhar-in-locals-olhar-em-globals resolução normal e encontrar o módulo pprint em globals.
Para disambiguate isso, você pode usar a palavra-chave "global". É claro que agora você já trabalhou passado o seu problema, e eu não sei se você realmente necessário "global" ou se alguma outra abordagem foi chamado para.