Donc, je reçois cette erreur
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
et vous pouvez voir que j'utilise la même déclaration d'importation plus loin et que cela fonctionne? Existe-t-il une règle non écrite concernant l'importation de circulaires? Comment utiliser la même classe plus loin dans la pile d'appels?
Je pense que la réponse de jpmc26, bien que nullement mal, soit trop lourde sur les importations circulaires. Ils peuvent fonctionner correctement si vous les configurez correctement.
La façon la plus simple de le faire est d'utiliser import my_module
_ syntaxe, plutôt que from my_module import some_object
. Le premier fonctionnera presque toujours, même si my_module
inclus nous importe en retour. Ce dernier ne fonctionne que si my_object
est déjà défini dans my_module
, qui dans une importation circulaire peut ne pas être le cas.
Pour être spécifique à votre cas: Essayez de changer entities/post.py
faire import physics
puis référez-vous à physics.PostBody
plutôt que simplement PostBody
directement. De même, changez physics.py
faire import entities.post
puis utilisez entities.post.Post
plutôt que simplement Post
.
Lorsque vous importez un module (ou un membre de celui-ci) pour la première fois, le code à l'intérieur du module est exécuté de manière séquentielle, comme tout autre code. Par exemple, il n’est pas traité différemment du corps d’une fonction. Un import
est simplement une commande comme une autre (affectation, appel de fonction, def
, class
). En supposant que vos importations se trouvent en haut du script, voici ce qui se passe:
World
à partir de world
, le script world
est exécuté.world
importe Field
, ce qui provoque le entities.field
script à exécuter.entities.post
script parce que vous avez essayé d'importer Post
entities.post
script provoque l'exécution du module physics
car il tente d'importer PostBody
physics
essaie d'importer Post
de entities.post
entities.post
Le module existe encore en mémoire, mais cela n'a pas d'importance. Soit le module n'est pas en mémoire, soit n'a pas encore de membre Post
car il n'a pas fini de s'exécuter pour définir Post
Post
n'est pas là pour être importé.Donc non, ce n'est pas "travailler plus haut dans la pile d'appels". Ceci est une trace de la pile de l'endroit où l'erreur s'est produite, ce qui signifie qu'elle a essayé de importer Post
dans cette classe. Vous ne devriez pas utiliser les importations circulaires. Au mieux, il présente un avantage négligeable (généralement non avantage) et pose de tels problèmes. Il incombe à tout développeur de l’entretenir, de le forcer à marcher sur des coquilles d’œufs pour éviter de le casser. Refactorisez votre organisation de module.
Pour comprendre les dépendances circulaires, vous devez vous rappeler que Python est essentiellement un langage de script. L'exécution des instructions en dehors des méthodes a lieu au moment de la compilation. Les instructions d'importation sont exécutées comme des appels de méthode. devrait penser à eux comme des appels de méthode.
Lorsque vous effectuez une importation, cela dépend si le fichier que vous importez existe déjà dans la table de module. Si tel est le cas, Python utilise tout ce qui se trouve actuellement dans la table des symboles. Sinon, Python commence à lire le fichier de module, à compiler/à exécuter/importer tout ce qu'il trouve. Les symboles référencés au moment de la compilation sont trouvés ou non, selon qu’ils ont été vus ou ne sont pas encore vus par le compilateur.
Imaginez que vous avez deux fichiers sources:
Fichier X.py
def X1:
return "x1"
from Y import Y2
def X2:
return "x2"
Fichier Y.py
def Y1:
return "y1"
from X import X1
def Y2:
return "y2"
Supposons maintenant que vous compiliez le fichier X.py. Le compilateur commence par définir la méthode X1, puis valide l'instruction d'importation dans X.py. Cela force le compilateur à suspendre la compilation de X.py et à commencer à compiler Y.py. Peu de temps après, le compilateur atteint l'instruction d'importation dans Y.py. Puisque X.py est déjà dans la table du module, Python utilise la table des symboles X.py incomplète existante pour satisfaire les références demandées. Tous les symboles apparaissant avant l'instruction d'importation dans X.py sont maintenant dans la table des symboles, mais les symboles suivants ne le sont pas. Comme X1 apparaît maintenant avant l'instruction d'importation, celle-ci a été importée avec succès. Python reprend alors la compilation de Y.py. Ce faisant, il définit Y2 et se termine. compiler Y.py.Elle reprend ensuite la compilation de X.py et trouve Y2 dans la table des symboles Y.py. La compilation termine finalement sans erreur.
Quelque chose de très différent se produit si vous essayez de compiler Y.py à partir de la ligne de commande. Lors de la compilation de Y.py, le compilateur accède à l’instruction d’importation avant de définir Y2. Ensuite, il commence à compiler X.py. Bientôt, il frappe la déclaration d'importation dans X.py qui nécessite Y2. Mais Y2 n'est pas défini et la compilation échoue.
Veuillez noter que si vous modifiez X.py pour importer Y1, la compilation réussira toujours, quel que soit le fichier que vous compilez. Cependant, si vous modifiez le fichier Y.py pour importer le symbole X2, aucun fichier ne sera compilé.
À tout moment où le module X, ou n'importe quel module importé par X, peut importer le module actuel, n'utilisez PAS:
from X import Y
Chaque fois que vous pensez qu'il y a une importation circulaire, vous devez également éviter les références de compilation à des variables dans d'autres modules. Considérez le code à la recherche innocent:
import X
z = X.Y
Supposons que le module X importe ce module avant que ce module importe X. En outre, supposons que Y soit défini dans X après l'instruction d'importation. Alors, Y ne sera pas défini lors de l'importation de ce module et vous obtiendrez une erreur de compilation. Si ce module importe Y en premier, vous pouvez vous en tirer. Mais si l'un de vos collègues modifie innocemment l'ordre des définitions dans un troisième module, le code est cassé.
Dans certains cas, vous pouvez résoudre les dépendances circulaires en déplaçant une instruction d'importation en dessous des définitions de symbole requises par d'autres modules. Dans les exemples ci-dessus, les définitions précédant l'instruction d'importation n'échouent jamais. Les définitions après l'instruction d'importation échouent parfois, en fonction de l'ordre de compilation. Vous pouvez même placer des instructions d'importation à la fin d'un fichier, à condition qu'aucun des symboles importés ne soit nécessaire lors de la compilation.
Notez que le déplacement des instructions d'importation dans un module masque ce que vous faites. Compensez cela avec un commentaire en haut de votre module, du type suivant:
#import X (actual import moved down to avoid circular dependency)
En général, c'est une mauvaise pratique, mais il est parfois difficile de l'éviter.
Pour ceux d'entre vous qui, comme moi, consultent ce numéro de Django, sachez que la documentation fournit une solution: https://docs.djangoproject.com/fr/1.10/ref/models/fields/ #foreignkey
"... Pour faire référence à des modèles définis dans une autre application, vous pouvez spécifier explicitement un modèle avec l'étiquette de l'application complète. Par exemple, si le modèle Fabricant ci-dessus est défini dans une autre application appelée Production, vous devez utiliser:
class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer',
on_delete=models.CASCADE,
)
Ce type de référence peut être utile lors de la résolution de dépendances d'importation circulaires entre deux applications. ... "
J'utilisais ce qui suit:
from module import Foo
foo_instance = Foo()
mais pour se débarrasser de circular reference
J'ai fait ce qui suit et cela a fonctionné:
import module.foo
foo_instance = foo.Foo()