Amanda, parte de nuestro equipo, nos hizo el favor de escribir este escrito porque domina perfectamente el tema.
Solución:
Creo que la respuesta es jpmc26, mientras que de ninguna manera incorrecto, se reduce demasiado a las importaciones circulares. Pueden funcionar bien, si los configura correctamente.
La forma más sencilla de hacerlo es utilizar import my_module
sintaxis, en lugar de 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 a su caso: intente cambiar entities/post.py
hacer import physics
y luego consulte physics.PostBody
en lugar de solo PostBody
directamente. Del mismo modo, cambie physics.py
hacer import entities.post
y luego usa entities.post.Post
en lugar de solo Post
.
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 al cuerpo de una función. Un import
es solo un comando como cualquier otro (asignación, una llamada de función, def
, class
). Suponiendo que sus importaciones ocurren en la parte superior del script, esto es lo que está sucediendo:
- Cuando intentas importar
World
deworld
, losworld
se ejecuta el script. - los
world
importaciones de scriptsField
, que causa laentities.field
script para ejecutar. - Este proceso continúa hasta llegar al
entities.post
script porque intentaste importarPost
- los
entities.post
causas de la secuencia de comandosphysics
módulo que se ejecutará porque intenta importarPostBody
- Finalmente,
physics
intenta importarPost
deentities.post
- No estoy seguro de si el
entities.post
El módulo todavía existe 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 ejecutar para definirPost
- De cualquier manera, se produce un error porque
Post
no está ahí 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 (por lo general, no 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.
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 de módulos. 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 tiempo de 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
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 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 ahora están en la tabla de símbolos, pero los símbolos posteriores no lo están. 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 use:
from X import Y
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:
import X
z = X.Y
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 debajo de las definiciones de símbolo 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:
#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.
Si sostienes alguna incertidumbre o disposición de progresar nuestro post puedes realizar una nota y con placer lo observaremos.