library - ¿Importación circular Python?
plotly python (4)
Creo que la respuesta actualmente aceptada por jpmc26 se reduce demasiado a las importaciones circulares. Pueden funcionar bien, si los configura correctamente.
La forma más sencilla de hacerlo es usar la sintaxis de import my_module
, en lugar from my_module import some_object
. El primero casi siempre funcionará, incluso si my_module
incluido nos importa de nuevo. Este último solo funciona si my_object
ya está definido en my_module
, que en una importación circular puede no ser el caso.
Para ser específico de su caso: intente cambiar entities/post.py
para import physics
y luego consulte physics.PostBody
lugar de solo PostBody
directamente. Del mismo modo, cambie physics.py
para hacer una import post
y luego use post.Post
lugar de solo Post
.
Así que estoy recibiendo 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 puedes ver que uso la misma declaración de importación más arriba y funciona? ¿Hay alguna regla no escrita sobre la importación circular? ¿Cómo uso la misma clase más abajo en la pila de llamadas?
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 manera diferente que el cuerpo de una función. Una import
es solo un comando como cualquier otro (asignación, llamada a una función, def
, class
). Suponiendo que sus importaciones se producen en la parte superior del script, esto es lo que está sucediendo:
- Cuando intenta importar
World
delworld
, se ejecuta la secuencia de comandosworld
. - El script del
world
importaField
, lo que hace que se ejecute el scriptentities.field
. - Este proceso continúa hasta que llegas al script
entities.post
porque has intentado importar - La secuencia de comandos
entities.post
hace que se ejecute el módulo dephysics
porque intenta importarPostBody
- Finalmente, la
physics
intenta importarPost
deentities.post
- No estoy seguro de si el módulo
entities.post
aún existe en la memoria, pero realmente no importa. O bien el módulo no está en la memoria, o el módulo aún no tiene un miembro dePost
porque no ha terminado de ejecutar para definirPost
- De cualquier forma, se produce un error porque el
Post
no está allí para ser importado
Así que no, no está "trabajando más arriba en la pila de llamadas". Este es un rastro de la pila donde ocurrió el error, lo que significa que cometió un error al tratar de importar el Post
en esa clase. No deberías usar importaciones circulares. En el mejor de los casos, tiene un beneficio insignificante (generalmente, no hay beneficio), y causa problemas como este. Carga a cualquier desarrollador que lo mantiene, obligándolos a caminar sobre cáscaras de huevo para evitar romperlo. Refactoriza tu organización de módulo.
Para aquellos de ustedes que, como yo, llegan a este tema desde Django, deben saber que los documentos brindan una solución: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey
"... Para referirse 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 del fabricante anterior se define en otra aplicación llamada producción, deberá usar:
class Car(models.Model):
manufacturer = models.ForeignKey(
''production.Manufacturer'',
on_delete=models.CASCADE,
)
Este tipo de referencia puede ser útil al resolver dependencias circulares de importación entre dos aplicaciones. ... "
Para comprender las dependencias circulares, debe recordar que Python es esencialmente un lenguaje de scripting. La ejecución de sentencias fuera de los métodos ocurre en tiempo de compilación. Los enunciados de importación se ejecutan como las llamadas a métodos, y para entenderlos debe pensar en ellos como en las 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 de módulos. Si lo hace, Python usa lo que se encuentre actualmente en la tabla de símbolos. Si no, 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 tiempo de compilación se encuentran o no, según hayan sido vistos o no por el compilador.
Imagina que tienes dos archivos fuente:
Archivo X.py
def X1:
return "x1"
from Y import Y2
def X2:
return "x2"
Archivo Y.py
def Y1:
return "y1"
from X import X1
def Y2:
return "y2"
Ahora supongamos que compila el archivo X.py. El compilador comienza definiendo el método X1 y luego acciona 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 acierta la declaración de importación en Y.py. Como 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 instrucción de importación en X.py están ahora en la tabla de símbolos, pero los símbolos posteriores no lo están. Como X1 ahora aparece antes de la declaración de importación, se importa con éxito. 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 completa el error w / o.
Algo muy diferente sucede si intenta compilar Y.py desde la línea de comando. Al compilar Y.py, el compilador golpea la declaración de importación antes de que defina Y2. Luego comienza a compilar X.py. Pronto llega al estado 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 tendrá éxito, 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á.
Siempre que el módulo X o cualquier módulo importado por X pueda importar el módulo actual, NO use:
from X import Y
Cada vez que crea que puede haber una importación circular, también debe evitar referencias de tiempo de compilación a variables en otros módulos. Considere el código de aspecto inocente:
import X
z = X.Y
Supongamos que el módulo X importa este módulo antes de que este módulo importe X. Además, supongamos 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 colaboradores inocentemente cambia 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 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, dependiendo del orden de compilación. Incluso puede colocar instrucciones de importación al final de un archivo, siempre que ninguno de los símbolos importados sea necesario en tiempo de compilación.
Tenga en cuenta que mover declaraciones de importación hacia abajo en un módulo oscurece lo que está haciendo. Compense esto con un comentario en la parte superior de su módulo, como el siguiente:
#import X (actual import moved down to avoid circular dependency)
En general, esta es una mala práctica, pero a veces es difícil de evitar.