Pregunta

Me encontré con este caso de UnboundLocalError recientemente, lo que parece extraño:

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()

Lo que produce:

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 vinculado en globals , y se enlazará en locals en la siguiente declaración. ¿Alguien puede ofrecer una explicación de por qué no está satisfecho resolver pprint al enlace en globals aquí?

Editar: Gracias a las buenas respuestas, puedo aclarar mi pregunta con la terminología pertinente:

En el momento de la compilación, el identificador pprint está marcado como local al marco. ¿El modelo de ejecución no tiene distinción donde dentro del marco al que está enlazado el identificador local? ¿Puede decir, "referirse al enlace global hasta esta instrucción de bytecode, en cuyo punto se ha rebotado a un enlace local"? ¿O el modelo de ejecución no tiene en cuenta esto?

¿Fue útil?

Solución

Parece que Python ve la línea from pprint import pprint y marca pprint como un nombre local de main () antes de ejecutando cualquier código. Como Python cree que pprint debería ser una variable local, debe hacer referencia a ella con pprint.pprint () antes de " asignar " con la declaración from..import , arroja ese error.

Eso es todo el sentido que puedo hacer de eso.

La moraleja, por supuesto, es poner siempre esas declaraciones import en la parte superior del alcance.

Otros consejos

¿Dónde está la sorpresa? Cualquier variable global para un ámbito que reasigne dentro de ese ámbito está marcada localmente por el compilador.

Si las importaciones se manejaran de manera diferente, eso sería sorprendente en este caso.

Sin embargo, puede ser un caso para no nombrar módulos después de los símbolos utilizados en ellos, o viceversa.

Bueno, eso fue lo suficientemente interesante para que experimentara un poco y leí http: / /docs.python.org/reference/executionmodel.html

Luego hice algunos retoques con tu código aquí y allá, esto es lo que pude 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()

salida:

<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'

En el método two () de pprint import pprint pero no reemplaza el nombre pprint en globals , ya que la palabra clave global no se utiliza en el ámbito de two () .

En el método three () , ya que no hay una declaración del nombre de pprint en el ámbito local, su valor predeterminado es el nombre global pprint , que es un módulo

Mientras que en main () , al principio se usa la palabra clave global , por lo que todas las referencias a pprint en El alcance del método main () se referirá al global nombre pprint . Lo que como podemos ver es un módulo al principio y está anulado en el global namespace con un método como lo hacemos con el de pprint import pprint

Aunque esto puede no estar respondiendo a la pregunta como tal, pero creo que es un hecho interesante.

=====================

Editar Otra cosa interesante.

Si tiene un módulo, diga:

mod1

from datetime import    datetime

def foo():
    print "bar"

y otro método dicen:

mod2

import  datetime
from mod1 import *

if __name__ == '__main__':
    print datetime.datetime.now()

que a primera vista parece ser correcto ya que ha importado el módulo datetime en mod2 .

ahora, si intenta ejecutar mod2 como script, se generará un error:

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 el segundo importado de mod2 import * ha invalidado el nombre datetime en el espacio de nombres, por lo tanto, el primer import datetime ya no es válido .

Moraleja: por lo tanto, el orden de las importaciones, la naturaleza de las importaciones (de x import *) y el conocimiento de las importaciones dentro de los módulos importados - importa .

Esta pregunta se respondió hace varias semanas, pero creo que puedo aclarar un poco las respuestas. Primero algunos hechos.

1: En Python,

import foo

es casi exactamente lo mismo que

foo = __import__("foo", globals(), locals(), [], -1)

2: al ejecutar código en una función, si Python encuentra una variable que aún no se ha definido en la función, busca en el ámbito global.

3: Python tiene una optimización que utiliza para las funciones llamadas " locals " ;. Cuando Python tokeniza una función, realiza un seguimiento de todas las variables que asigna. Asigna a cada una de estas variables un número de un entero local monótonamente creciente. Cuando Python ejecuta la función, crea una matriz con tantas ranuras como variables locales, y asigna a cada ranura un valor especial que significa que "aún no se ha asignado", y ahí es donde se almacenan los valores de esas variables. . Si hace referencia a un local que aún no se ha asignado, Python ve ese valor especial y lanza una excepción UnboundLocalValue.

El escenario está listo. Tu " desde pprint import pprint " Es realmente una forma de asignación. Así que Python crea una variable local llamada " pprint " que ocluye la variable global. Luego, cuando te refieres a " pprint.pprint " en la función, pulsas el valor especial y Python lanza la excepción. Si no tuviera esa declaración de importación en la función, Python usaría la resolución normal look-in-locals-first-look-in-globals y buscaría el módulo de impresión en globals.

Para desambiguar esto, puede usar " global " palabra clave. Por supuesto, ya ha superado su problema y no sé si realmente necesitaba " global " o si se requiere algún otro enfoque.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top