Entonces recibo este error
Traceback (most recent call last):
File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module>
from world import World
File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module>
from entities.field import Field
File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module>
from entities.goal import Goal
File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module>
from entities.post import Post
File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module>
from physics import PostBody
File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module>
from entities.post import Post
ImportError: cannot import name Post
y puede ver que utilizo la misma declaración de importación más arriba y funciona? ¿Existe alguna regla no escrita sobre la importación circular? ¿Cómo uso la misma clase más abajo en la pila de llamadas?
from
sintaxis siempre funcionará. Si tengoclass A(object): pass; class C(b.B): pass
en el módulo A yclass B(a.A): pass
en el módulo B, entonces la importación circular sigue siendo un problema y esto no funcionará.B
al móduloa
o mueva la claseC
al módulob
para que pueda romper el ciclo. También vale la pena señalar que incluso si solo una dirección del círculo tiene código de nivel superior involucrado (por ejemplo, si la claseC
no existe), es posible que obtenga un error, dependiendo de qué módulo fue importado primero por otro código.from . import sibling_module
, nofrom .sibling_module import SomeClass
). Hay algo más de sutileza cuando el__init__.py
archivo de un paquete está involucrado en la importación circular, pero el problema es poco común y probablemente un error en laimport
implementación. Vea el error 23447 de Python , para el que envié un parche (que lamentablemente ha estado languideciendo).Cuando importa un módulo (o un miembro de él) por primera vez, el código dentro del módulo se ejecuta secuencialmente como cualquier otro código; por ejemplo, no se trata de forma diferente al cuerpo de una función. Una
import
es simplemente un comando como cualquier otra (asignación, una llamada de función,def
,class
). Suponiendo que sus importaciones se produzcan en la parte superior del script, esto es lo que está sucediendo:World
desdeworld
,world
se ejecuta el script.world
script se importaField
, lo que haceentities.field
que se ejecute.entities.post
script porque intentó importarPost
entities.post
script hacephysics
que se ejecute el módulo porque intenta importarPostBody
physics
intenta importarPost
desdeentities.post
entities.post
módulo existe todavía en la memoria, pero realmente no importa. O el módulo no está en la memoria o el módulo aún no tiene unPost
miembro porque no ha terminado de ejecutarse para definirPost
Post
no está allí para ser importado.Entonces no, no está "trabajando más arriba en la pila de llamadas". Este es un seguimiento de la pila de dónde ocurrió el error, lo que significa que se produjo un error al intentar importar
Post
en esa clase. No debería utilizar importaciones circulares. En el mejor de los casos, tiene un beneficio insignificante (normalmente, ningún beneficio) y causa problemas como este. Carga a cualquier revelador manteniéndola, obligándolos a caminar sobre cáscaras de huevo para no romperla. Refactorice la organización de su módulo.fuente
isinstance(userData, Post)
. Independientemente, no tienes otra opción. La importación circular no funcionará. El hecho de que tenga importaciones circulares es un olor a código para mí. Sugiere que tiene alguna funcionalidad que debería trasladarse a un tercer módulo. No podría decir qué sin mirar ambas clases enteras.def
no se ejecuta hasta que se llama a la función, por lo que la importación no se producirá hasta que usted realmente llame a la función. Para entonces, elimport
s debería funcionar ya que uno de los módulos se habría importado completamente antes de la llamada. Ese es un truco absolutamente repugnante, y no debería permanecer en su base de código durante un período de tiempo significativo.import foo
lugar de hacerlofrom foo import Bar
. Eso es porque la mayoría de los módulos simplemente definen cosas (como funciones y clases) que se ejecutan más tarde. Los módulos que hacen cosas importantes cuando los importa (como un script no protegido porif __name__ == "__main__"
) pueden seguir siendo un problema, pero eso no es demasiado común.Para comprender las dependencias circulares, debe recordar que Python es esencialmente un lenguaje de secuencias de comandos. La ejecución de declaraciones fuera de los métodos se produce en tiempo de compilación. Las instrucciones de importación se ejecutan como llamadas a métodos, y para comprenderlas, debe pensar en ellas como llamadas a métodos.
Cuando realiza una importación, lo que sucede depende de si el archivo que está importando ya existe en la tabla del módulo. Si es así, Python usa lo que esté actualmente en la tabla de símbolos. Si no es así, Python comienza a leer el archivo del módulo, compilando / ejecutando / importando lo que encuentre allí. Los símbolos a los que se hace referencia en el momento de la compilación se encuentran o no, dependiendo de si se han visto o el compilador aún no los ha visto.
Imagina que tienes dos archivos fuente:
Archivo X.py
Archivo Y.py
Ahora suponga que compila el archivo X.py. El compilador comienza definiendo el método X1 y luego ingresa a la declaración de importación en X.py. Esto hace que el compilador pause la compilación de X.py y comience a compilar Y.py. Poco después, el compilador accede a la declaración de importación en Y.py. Dado que X.py ya está en la tabla de módulos, Python usa la tabla de símbolos X.py incompleta existente para satisfacer las referencias solicitadas. Todos los símbolos que aparecen antes de la declaración de importación en X.py están ahora en la tabla de símbolos, pero los símbolos posteriores no. Dado que X1 ahora aparece antes de la declaración de importación, se importa correctamente. Python luego reanuda la compilación de Y.py. Al hacerlo, define Y2 y termina de compilar Y.py. Luego reanuda la compilación de X.py y encuentra Y2 en la tabla de símbolos Y.py. La compilación finalmente se completa sin errores.
Algo muy diferente sucede si intenta compilar Y.py desde la línea de comandos. Mientras compila Y.py, el compilador llega a la declaración de importación antes de definir Y2. Luego comienza a compilar X.py. Pronto llega a la declaración de importación en X.py que requiere Y2. Pero Y2 no está definido, por lo que la compilación falla.
Tenga en cuenta que si modifica X.py para importar Y1, la compilación siempre se realizará correctamente, independientemente del archivo que compile. Sin embargo, si modifica el archivo Y.py para importar el símbolo X2, ninguno de los archivos se compilará.
En cualquier momento en que el módulo X, o cualquier módulo importado por X pueda importar el módulo actual, NO utilice:
Siempre que crea que puede haber una importación circular, también debe evitar compilar referencias de tiempo a variables en otros módulos. Considere el código de apariencia inocente:
Suponga que el módulo X importa este módulo antes de que este módulo importe X. Además, suponga que Y se define en X después de la declaración de importación. Entonces, Y no se definirá cuando se importe este módulo y obtendrá un error de compilación. Si este módulo importa Y primero, puede salirse con la suya. Pero cuando uno de sus compañeros de trabajo cambia inocentemente el orden de las definiciones en un tercer módulo, el código se romperá.
En algunos casos, puede resolver dependencias circulares moviendo una declaración de importación hacia abajo debajo de las definiciones de símbolos que necesitan otros módulos. En los ejemplos anteriores, las definiciones antes de la declaración de importación nunca fallan. Las definiciones posteriores a la declaración de importación a veces fallan, según el orden de compilación. Incluso puede colocar declaraciones de importación al final de un archivo, siempre que no se necesite ninguno de los símbolos importados en el momento de la compilación.
Tenga en cuenta que mover las declaraciones de importación hacia abajo en un módulo oscurece lo que está haciendo. Compensa esto con un comentario en la parte superior de tu módulo, algo como lo siguiente:
En general, esta es una mala práctica, pero a veces es difícil de evitar.
fuente
Para aquellos de ustedes que, como yo, ven este problema desde Django, deben saber que los documentos brindan una solución: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey
"... Para hacer referencia a los modelos definidos en otra aplicación, puede especificar explícitamente un modelo con la etiqueta de la aplicación completa. Por ejemplo, si el modelo de fabricante anterior se define en otra aplicación llamada producción, deberá utilizar:
Este tipo de referencia puede resultar útil para resolver dependencias de importación circulares entre dos aplicaciones. ... "
fuente
Pude importar el módulo dentro de la función (solo) que requeriría los objetos de este módulo:
fuente
Si se encuentra con este problema en una aplicación bastante compleja, puede resultar engorroso refactorizar todas sus importaciones. PyCharm ofrece una solución rápida para esto que también cambiará automáticamente todo el uso de los símbolos importados.
fuente
Estaba usando lo siguiente:
pero para deshacerme de
circular reference
hice lo siguiente y funcionó:fuente