with nonlocal modulenotfounderror imports how from for python binding scope identifier

modulenotfounderror - python nonlocal



Python globales, lugareƱos y UnboundLocalError (4)

¿Dónde está la sorpresa? El compilador marca localmente en ese ámbito cualquier variable global a un ámbito que reasigne dentro de dicho ámbito.

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

Puede hacer un caso para no nombrar módulos después de los símbolos utilizados en el mismo, o viceversa.

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

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 ligado en globals , y estará ligado en locals en la siguiente declaración. ¿Alguien puede ofrecer una explicación de por qué no es feliz resolver pprint para el enlace en globals aquí?

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

En tiempo de compilación, el identificador pprint se marca como local para el marco. ¿El modelo de ejecución no tiene distinción donde dentro del marco está vinculado el identificador local? ¿Puede decir "se refiere a la vinculación global hasta esta instrucción de código de bytes, en cuyo punto se ha recuperado a un enlace local" o el modelo de ejecución no cuenta para esto?


Bueno, eso fue lo suficientemente interesante como para experimentar un poco y lo leí a través de http://docs.python.org/reference/executionmodel.html

Luego hice algunos retoques con su 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() from pprint import pprint pero no reemplaza el nombre pprint en globals , ya que la palabra clave global no se usa en el ámbito de two() .

En el método three() ya que no hay una declaración del nombre de la pprint en el ámbito local, se toma por defecto el nombre global pprint que es un módulo

Mientras que en main() , al principio se utiliza la palabra clave global por lo que todas las referencias a pprint en el ámbito del método main() se referirán al nombre global pprint . Lo cual, como podemos ver, es un módulo al principio y se anula en el namespace global con un método, como lo hacemos from pprint import pprint

Aunque puede que no responda 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 dice:

mod2

import datetime from mod1 import * if __name__ == ''__main__'': print datetime.datetime.now()

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

ahora si intenta ejecutar mod2 como un script lanzará 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 la segunda importación from mod2 import * ha from mod2 import * el nombre datetime en el espacio de nombres, por lo tanto, la primera import datetime ya no es válida.

Moral: Por lo tanto, importa 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.


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

Eso es todo el sentido que puedo tener.

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


Esta pregunta fue respondida 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: cuando se ejecuta código en una función, si Python encuentra una variable que todavía no se ha definido en la función, se ve en el alcance global.

3: Python tiene una optimización que usa para funciones llamadas "locales". Cuando Python tokenizes 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 "no se ha asignado todavía", y ahí es donde se almacenan los valores para esas variables. Si hace referencia a un local que aún no ha sido asignado, Python ve ese valor especial y arroja una excepción de UnboundLocalValue.

El escenario ahora está establecido. Su "de pprint import pprint" es realmente una forma de asignación. Entonces Python crea una variable local llamada "pprint" que ocluye la variable global. Luego, cuando te refieres a "pprint.pprint" en la función, tocas el valor especial y Python lanza la excepción. Si no tuvieras esa declaración de importación en la función, Python usaría la resolución normal look-in-locals-first-then-look-in-globals y encontraría el módulo pprint en globales.

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