¿Qué sucederá si dos módulos se importan entre sí?
Para generalizar el problema, ¿qué pasa con las importaciones cíclicas en Python?
¿Qué sucederá si dos módulos se importan entre sí?
Para generalizar el problema, ¿qué pasa con las importaciones cíclicas en Python?
Respuestas:
Hubo una muy buena discusión sobre esto en comp.lang.python el año pasado. Responde tu pregunta bastante a fondo.
fuente
Si lo haces por
import foodentrobary porimport bardentrofoo, funcionará bien. Para cuando realmente se ejecute algo, ambos módulos estarán completamente cargados y tendrán referencias mutuas.El problema es cuando en cambio lo haces
from foo import abcyfrom bar import xyz. Porque ahora cada módulo requiere que el otro módulo ya esté importado (para que exista el nombre que estamos importando) antes de que pueda importarse.fuente
from foo import *yfrom bar import *también funcionará bien.from x import y, y aún así recibe el error de importación circularimportse ejecute la instrucción. Por lo tanto, no se producirá un error, pero es posible que no obtenga todas las variables que espera.from foo import *yfrom bar import *, todo lo ejecutado en elfoose encuentra en la fase de inicialización debar, y las funciones reales enbaraún no se ha definido ...Las importaciones cíclicas finalizan, pero debe tener cuidado de no utilizar los módulos importados cíclicamente durante la inicialización del módulo.
Considere los siguientes archivos:
a.py:
b.py:
Si ejecuta a.py, obtendrá lo siguiente:
En la segunda importación de b.py (en la segunda
a in), el intérprete de Python no importabnuevamente, porque ya existe en el módulo dict.Si intenta acceder
b.xdesde laainicialización del módulo, obtendrá unAttributeError.Agregue la siguiente línea a
a.py:Entonces, la salida es:
Esto se debe a que los módulos se ejecutan en la importación y en el momento en que
b.xse accede, la líneax = 3aún no se ha ejecutado, lo que solo sucederá despuésb out.fuente
__name__lugar de'a'. Al principio, estaba totalmente confundido por qué un archivo se ejecutaría dos veces.Como otras respuestas describen, este patrón es aceptable en Python:
Lo que evitará la ejecución de la declaración de importación cuando el archivo sea importado por otros módulos. Solo si hay una dependencia circular lógica, esto fallará.
La mayoría de las importaciones circulares no son realmente importaciones circulares lógicas, sino que generan
ImportErrorerrores, debido a la forma en queimport()evalúa las declaraciones de nivel superior de todo el archivo cuando se llama.Estos
ImportErrorscasi siempre se pueden evitar si quieres positivamente sus importaciones en la parte superior :Considere esta importación circular:
App A
App B
De David Beazleys excelente charla Módulos y paquetes: ¡Vive y deja morir! - PyCon 2015 ,
1:54:00aquí hay una manera de lidiar con las importaciones circulares en python:Esto intenta importar
SimplifiedImageSerializery siImportErrorse genera, porque ya está importado, lo extraerá del caché de importación.PD: Tienes que leer esta publicación completa con la voz de David Beazley.
fuente
¡Tengo un ejemplo aquí que me llamó la atención!
foo.py
bar.py
main.py
En la línea de comando: $ python main.py
fuente
import barenfoo.pyel finalbaryfooambos deben usargX, la solución 'más limpia' es colocargXotro módulo y tener ambosfooebarimportar ese módulo. (más limpio en el sentido de que no hay dependencias semánticas ocultas).barni siquiera puede encontrargXen el foo. la importación circular está bien por sí misma, pero es solo quegXno está definida cuando se importa.Módulo a.py:
Módulo b.py
Ejecutar "Módulo a" generará:
Emitió estas 3 líneas mientras se suponía que debía generar infinitival debido a la importación circular. Lo que sucede línea por línea mientras se ejecuta el "Módulo a" se enumera aquí:
import b. entonces visitará el módulo bimport a. entonces visitará el módulo aimport bpero tenga en cuenta que esta línea ya no se ejecutará nuevamente , porque cada archivo en python ejecuta una línea de importación solo por una vez, no importa dónde o cuándo se ejecute. entonces pasará a la siguiente línea e imprimirá"This is from module a"."This is from module b""This is from module a"y se terminará el programa.fuente
Estoy completamente de acuerdo con la respuesta de Pythoneer aquí. Pero me topé con un código que estaba defectuoso con las importaciones circulares y causó problemas al intentar agregar pruebas unitarias. Entonces, para parchearlo rápidamente sin cambiar todo, puede resolver el problema haciendo una importación dinámica.
Nuevamente, esto no es una solución permanente, pero puede ayudar a alguien que quiera corregir un error de importación sin cambiar demasiado el código.
¡Salud!
fuente
Hay muchas respuestas geniales aquí. Si bien generalmente hay soluciones rápidas al problema, algunas de las cuales se sienten más pitónicas que otras, si tiene el lujo de refactorizar, otro enfoque es analizar la organización de su código e intentar eliminar la dependencia circular. Puede encontrar, por ejemplo, que tiene:
Archivo a.py
Archivo b.py
En este caso, solo mueve un método estático a un archivo separado, por ejemplo
c.py:Archivo c.py
permitirá eliminar el
save_resultmétodo de A y, por lo tanto, permitirá eliminar la importación de A de a en b:Archivo refactorizado a.py
Archivo refactorizado b.py
En resumen, si tiene una herramienta (por ejemplo, pylint o PyCharm) que informa sobre métodos que pueden ser estáticos, simplemente lanzar un
staticmethoddecorador sobre ellos podría no ser la mejor manera de silenciar la advertencia. Aunque el método parece estar relacionado con la clase, podría ser mejor separarlo, especialmente si tiene varios módulos estrechamente relacionados que podrían necesitar la misma funcionalidad y tiene la intención de practicar los principios DRY.fuente
Las importaciones circulares pueden ser confusas porque la importación hace dos cosas:
El primero se realiza solo una vez, mientras que el segundo en cada declaración de importación. La importación circular crea una situación cuando el módulo de importación usa uno importado con código parcialmente ejecutado. En consecuencia, no verá los objetos creados después de la declaración de importación. Debajo de la muestra de código lo demuestra.
Las importaciones circulares no son el mal supremo que debe evitarse a toda costa. En algunos marcos como Flask son bastante naturales y ajustar su código para eliminarlos no mejora el código.
main.py
b. por
a.py
Python main.py salida con comentarios
fuente
Resolví el problema de la siguiente manera, y funciona bien sin ningún error. Considere dos archivos
a.pyyb.py.Agregué esto
a.pyy funcionó.a.py:
b.py:
La salida que obtengo es
fuente
Ok, creo que tengo una solución genial. Digamos que tiene archivo
ay archivob. Usted tiene unadefoclassen el archivobque desea utilizar en el móduloa, pero hay algo más, o bien unadef,classo una variable de archivoaque usted necesita en su definición o clase en el archivob. Lo que puede hacer es, al final del archivoa, después de llamar a la función o clase en el archivoaque se necesita en el archivob, pero antes de llamar a la función o clase desde el archivobque necesita para el archivoa, digaimport bEntonces, y aquí está la parte clave , en todas las definiciones o clases en el archivobque necesitan el archivodefoclassdesdea(llamémosloCLASS), dicesfrom a import CLASSEsto funciona porque puede importar archivos
bsin que Python ejecute ninguna de las declaraciones de importación en el archivoby, por lo tanto, elude las importaciones circulares.Por ejemplo:
Presentar un:
Archivo b:
Voila
fuente
from a import CLASSen realidad no omite ejecutar todo el código en a.py. Esto es lo que realmente sucede: (1) Todo el código en a.py se ejecuta como un módulo especial "__main__". (2) Enimport b, se ejecuta el código de nivel superior en b.py (que define la clase B) y luego el control vuelve a "__main__". (3) "__main__" finalmente pasa el control ago.dostuff(). (4) cuando llega dostuff ()import a, ejecuta todo el código en a.py nuevamente , esta vez como el módulo "a"; luego importa el objeto CLASS del nuevo módulo "a". Entonces, en realidad, esto funcionaría igual de bien si lo usaraimport aen cualquier parte de b.py.