script para paquetes modulos lista librerias instalar importar descargar clases bibliotecas python python-import cyclic

para - Dependencias de módulos cíclicos e importaciones relativas en Python



modulos python 3 (3)

Supongamos que tenemos dos módulos con dependencias cíclicas:

# a.py import b def f(): return b.y x = 42

# b.py import a def g(): return a.x y = 43

Los dos módulos están en el directorio pkg con un __init__.py vacío. La importación de pkg.a o pkg.b funciona bien, como se explica en esta respuesta . Si cambio las importaciones a importaciones relativas

from . import b

Obtengo un ImportError cuando intento importar uno de los módulos:

>>> import pkg.a Traceback (most recent call last): File "<stdin>", line 1, in <module> File "pkg/a.py", line 1, in <module> from . import b File "pkg/b.py", line 1, in <module> from . import a ImportError: cannot import name a

¿Por qué obtengo este error? ¿No es la situación más o menos la misma que la anterior? (¿Esto está relacionado con esta pregunta ?)

Editar : esta pregunta no es sobre diseño de software. Soy consciente de las formas de evitar la dependencia circular, pero de todos modos estoy interesado en el motivo del error.


(Incedentally, la importación relativa no importa. El uso from pkg import ... revela la misma excepción).

Creo que lo que está sucediendo aquí es que la diferencia entre from foo import bar e import foo.bar es que en el primero, la bar valor podría ser module en pkg foo o podría ser una variable en un foo módulo. En el segundo caso, no es válido que la bar sea ​​otra cosa que un módulo / paquete.

Esto sería importante porque si se sabe que la barra es un módulo, entonces el contenido de sys.modules es suficiente para poblarlo. Si puede ser una variable en el módulo foo , entonces el intérprete debe mirar los contenidos de foo , pero al importar foo , eso no sería válido; el módulo real no se ha completado aún.

En el caso de una importación relativa, entendemos from . import bar from . import bar significa importar el módulo de barra del paquete que contiene el módulo actual, pero esto es realmente solo azúcar sintáctico, el . el nombre se traduce a un nombre completamente calificado y se pasa a __import__() , y por lo tanto se ve por todo el mundo como ambigious from foo import bar


Como nota adicional:

Tenía la siguiente estructura de módulo:

base +guiStuff -gui +databaseStuff -db -basescript

Quería poder ejecutar mi script mediante import base.basescript , sin embargo, esto falló con un error ya que el archivo gui tenía una import base.databaseStuff.db que causaba una importación de base . Como la base solo se registró como __main__ causó una segunda ejecución de las importaciones completas y el error anterior a menos que use una secuencia de comandos externa por encima de la base , importando base / basescript solo una vez. Para evitar esto pongo lo siguiente en mi script base:

if __name__ == ''__main__'' or / not ''__main__'' in sys.modules or / sys.modules[''__main__''].__file__ != __file__: #imports here


Primero, comencemos con cómo from import trabajo de from import en Python:

Bueno, primero veamos el código de bytes:

>>> def foo(): ... from foo import bar >>> dis.dis(foo) 2 0 LOAD_CONST 1 (-1) 3 LOAD_CONST 2 ((''bar'',)) 6 IMPORT_NAME 0 (foo) 9 IMPORT_FROM 1 (bar) 12 STORE_FAST 0 (bar) 15 POP_TOP 16 LOAD_CONST 0 (None) 19 RETURN_VALUE

hmm interesante :), así que from foo import bar se traduce al primer IMPORT_NAME foo que equivale a import foo y luego a la IMPORT_FROM bar .

Ahora, ¿ IMPORT_FROM hace IMPORT_FROM ?

veamos qué hace python cuando encuentra IMPORT_FROM :

TARGET(IMPORT_FROM) w = GETITEM(names, oparg); v = TOP(); READ_TIMESTAMP(intr0); x = import_from(v, w); READ_TIMESTAMP(intr1); PUSH(x); if (x != NULL) DISPATCH(); break;

Bueno, básicamente, él obtiene los nombres para importar, que en nuestra función foo() será bar , luego emerge de la pila de marcos el valor v que es la devolución del último código de operación ejecutado que es IMPORT_NAME , luego llama al function import_from() con estos dos argumentos:

static PyObject * import_from(PyObject *v, PyObject *name) { PyObject *x; x = PyObject_GetAttr(v, name); if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Format(PyExc_ImportError, "cannot import name %S", name); } return x; }

Como puede ver la función import_from() es muy fácil, primero intente obtener el name del atributo del módulo v , si no existe, suba ImportError contrario devuelva este atributo.

Ahora, ¿qué tiene esto que ver con la importación relativa?

Bien relativo de importación como from . import b from . import b son equivalentes, por ejemplo, en el caso que está en la pregunta OP a from pkg import b .

¿Pero cómo sucede esto? Para entender esto deberíamos echarle un vistazo al módulo import.c de python especialmente a la función get_parent() . Como puede ver, la función es muy larga para enumerarla aquí, pero en general lo que hace cuando ve una importación relativa es intentar reemplazar el punto . con el paquete principal dependiendo del módulo __main__ , que es nuevamente de la pregunta OP es el paquete pkg .

Ahora, intentemos todo esto y tratemos de descubrir por qué terminamos con el comportamiento en la pregunta OP.

Por esto, nos ayudará si podemos ver qué hace python cuando hacemos importaciones, bueno, es nuestro pitón de la suerte que ya viene con esta característica que puede habilitarse al ejecutarlo en modo extra verbose -vv .

Entonces, usando la línea de comando: python -vv -c ''import pkg.b'' :

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. import pkg # directory pkg # trying pkg/__init__.so # trying pkg/__init__module.so # trying pkg/__init__.py # pkg/__init__.pyc matches pkg/__init__.py import pkg # precompiled from pkg/__init__.pyc # trying pkg/b.so # trying pkg/bmodule.so # trying pkg/b.py # pkg/b.pyc matches pkg/b.py import pkg.b # precompiled from pkg/b.pyc # trying pkg/a.so # trying pkg/amodule.so # trying pkg/a.py # pkg/a.pyc matches pkg/a.py import pkg.a # precompiled from pkg/a.pyc # clear[2] __name__ # clear[2] __file__ # clear[2] __package__ # clear[2] __name__ # clear[2] __file__ # clear[2] __package__ ... Traceback (most recent call last): File "<string>", line 1, in <module> File "pkg/b.py", line 1, in <module> from . import a File "pkg/a.py", line 2, in <module> from . import a ImportError: cannot import name a # clear __builtin__._

¿Qué pasa justo antes de ImportError ?

Primero) from . import a se llama a la from . import a en pkg/b.py , que se traduce como se explicó anteriormente a from pkg import a , que de nuevo en bytecode es equivalente a import pkg; getattr(pkg, ''a'') import pkg; getattr(pkg, ''a'') . Pero espere un minuto, ¿es un módulo también? Bueno, aquí viene la parte divertida si tenemos algo así como from module|package import module en este caso, se producirá una segunda importación, que es la importación del módulo en la cláusula de importación. Entonces, de nuevo, en el ejemplo de OP, ahora necesitamos importar pkg/a.py , y como saben, antes de nada, configuramos en nuestros sys.modules una clave para nuestro nuevo módulo que será pkg.a y luego continuamos nuestra interpretación de el módulo pkg/a.py , pero antes de que el módulo pkg/a.py termine de importarlo llame from . import b from . import b .

Ahora viene la Segunda parte ) , se importará pkg/b.py y, a su vez, primero intentará import pkg ya que pkg ya está importado, por lo que hay un pkg clave en nuestros sys.modules que devolverá el valor de esa llave. Luego import b configurará la clave sys.modules en sys.modules y comenzará la interpretación. Y llegamos a esta línea from . import a from . import a !

Pero recuerde que pkg/a.py ya se importó, lo que significa (''pkg.a'' in sys.modules) == True por lo que se omitirá la importación y solo se getattr(pkg, ''a'') , pero pasará ? Python no terminó de importar pkg/a.py !? Entonces solo se getattr(pkg, ''a'') , y esto generará un AttributeError en la función import_from() , que se traducirá a ImportError(cannot import name a) .

DESCARGO DE RESPONSABILIDAD : Este es mi propio esfuerzo para comprender lo que sucede dentro del intérprete, estoy muy lejos de ser un experto.

EDIt: Esta respuesta fue reformulada porque cuando traté de leerla de nuevo, comenté que mi respuesta estaba mal formulada, espero que ahora sea más útil :)