Que se passera-t-il si deux modules s’importent?
Pour généraliser le problème, qu'en est-il des importations cycliques en Python?
Que se passera-t-il si deux modules s’importent?
Pour généraliser le problème, qu'en est-il des importations cycliques en Python?
Réponses:
L' an dernier, il y a eu une très bonne discussion à ce sujet sur comp.lang.python . Cela répond assez bien à votre question.
la source
Si vous faites à l'
import foo
intérieurbar
et à l'import bar
intérieurfoo
, cela fonctionnera bien. Au moment où quelque chose s'exécute réellement, les deux modules seront entièrement chargés et auront des références l'un à l'autre.Le problème est quand vous le faites
from foo import abc
etfrom bar import xyz
. Parce que maintenant chaque module nécessite que l'autre module soit déjà importé (pour que le nom que nous importons existe) avant de pouvoir être importé.la source
from foo import *
etfrom bar import *
sera également très bien.from x import y
, mais obtient toujours l'erreur d'importation circulaireimport
exécution de l' instruction. Il n'y aura donc pas d'erreur, mais vous n'obtiendrez peut-être pas toutes les variables que vous attendez.from foo import *
etfrom bar import *
, tout exécuté dans lefoo
est dans la phase d'initialisation debar
, et les fonctions réelles dansbar
n'ont pas encore été définies ...Les importations cycliques se terminent, mais vous devez faire attention à ne pas utiliser les modules importés cycliquement lors de l'initialisation du module.
Considérez les fichiers suivants:
a.py:
b.py:
Si vous exécutez a.py, vous obtiendrez les éléments suivants:
Lors de la seconde importation de b.py (dans la seconde
a in
), l'interpréteur Python ne réimporterab
pas, car il existe déjà dans le module dict.Si vous essayez d'accéder à
b.x
partir dea
lors de l'initialisation du module, vous obtiendrez unAttributeError
.Ajoutez la ligne suivante à
a.py
:Ensuite, la sortie est:
En effet, les modules sont exécutés lors de l'importation et au moment de l'
b.x
accès, la lignex = 3
n'a pas encore été exécutée, ce qui ne se produira qu'aprèsb out
.la source
__name__
place de'a'
. Au début, je ne savais vraiment pas pourquoi un fichier serait exécuté deux fois.Comme d'autres réponses décrivent ce modèle est acceptable en python:
Ce qui évitera l'exécution de l'instruction import lorsque le fichier est importé par d'autres modules. Seulement s'il existe une dépendance circulaire logique, cela échouera.
La plupart des importations circulaires ne sont pas en fait des importations circulaires logiques, mais génèrent plutôt des
ImportError
erreurs, en raison de la façon dontimport()
les instructions de niveau supérieur du fichier entier sont appelées.Ceux-ci
ImportErrors
peuvent presque toujours être évités si vous voulez que vos importations soient au top :Considérez cette importation circulaire:
App A
App B
De David Beazleys excellent talk Modules et packages: Live and Let Die! - PyCon 2015 ,
1:54:00
voici une façon de traiter les importations circulaires en python:Cela essaie d'importer
SimplifiedImageSerializer
et s'ilImportError
est levé, car il est déjà importé, il le tirera du cache d'importation.PS: Vous devez lire l'intégralité de cet article dans la voix de David Beazley.
la source
J'ai ici un exemple qui m'a frappé!
foo.py
bar.py
main.py
Sur la ligne de commande: $ python main.py
la source
import bar
àfoo.py
la finbar
et lesfoo
deux doivent utilisergX
, la solution la plus «propre» consiste à mettregX
dans un autre module et à avoir les deuxfoo
et àbar
importer ce module. (le plus propre dans le sens où il n'y a pas de dépendances sémantiques cachées.)bar
ne trouve même pasgX
dans le foo. l'importation circulaire est bonne en soi, mais c'est juste que cegX
n'est pas défini quand il est importé.Module a.py:
Module b.py
L'exécution du "Module a" produira:
Il a sorti ces 3 lignes alors qu'il était supposé sortir infinitival à cause de l'importation circulaire. Ce qui se passe ligne par ligne lors de l'exécution du "Module a" est répertorié ici:
import b
. il visitera donc le module bimport a
. il visitera donc le module aimport b
mais notez que cette ligne ne sera plus exécutée , car chaque fichier en python exécute une ligne d'importation juste pour une fois, peu importe où ou quand elle est exécutée. il passera donc à la ligne suivante et s'imprimera"This is from module a"
."This is from module b"
"This is from module a"
et le programme sera terminé.la source
Je suis entièrement d'accord avec la réponse de pythoneer ici. Mais je suis tombé sur un code qui était imparfait avec les importations circulaires et a causé des problèmes lors de l'ajout de tests unitaires. Donc, pour le corriger rapidement sans tout changer, vous pouvez résoudre le problème en effectuant une importation dynamique.
Encore une fois, ce n'est pas un correctif permanent, mais peut aider quelqu'un qui souhaite corriger une erreur d'importation sans trop modifier le code.
À votre santé!
la source
Il y a beaucoup de bonnes réponses ici. Bien qu'il existe généralement des solutions rapides au problème, dont certaines semblent plus pythoniques que d'autres, si vous avez le luxe de refactoriser, une autre approche consiste à analyser l'organisation de votre code et à supprimer la dépendance circulaire. Vous pouvez par exemple constater que vous avez:
Fichier a.py
Fichier b.py
Dans ce cas, il suffit de déplacer une méthode statique vers un fichier distinct, par exemple
c.py
:Fichier c.py
permettra de supprimer la
save_result
méthode de A, et donc de supprimer l'importation de A de a dans b:Fichier refacturé a.py
Fichier refacturé b.py
En résumé, si vous avez un outil (par exemple pylint ou PyCharm) qui rend compte des méthodes qui peuvent être statiques, il suffit de jeter un
staticmethod
décorateur dessus pour ne pas être le meilleur moyen de faire taire l'avertissement. Même si la méthode semble liée à la classe, il pourrait être préférable de la séparer, surtout si vous avez plusieurs modules étroitement liés qui pourraient avoir besoin de la même fonctionnalité et que vous avez l'intention de pratiquer les principes DRY.la source
Les importations circulaires peuvent prêter à confusion car l'importation signifie deux choses:
Le premier est effectué une seule fois, tandis que le second à chaque déclaration d'importation. L'importation circulaire crée une situation lorsque le module d'importation utilise un module importé avec du code partiellement exécuté. En conséquence, il ne verra pas les objets créés après l'instruction d'importation. L'exemple de code ci-dessous le montre.
Les importations circulaires ne sont pas le mal ultime à éviter à tout prix. Dans certains frameworks comme Flask, ils sont assez naturels et peaufiner votre code pour les éliminer ne le rend pas meilleur.
main.py
b.by
a.py
sortie python main.py avec commentaires
la source
J'ai résolu le problème de la manière suivante, et cela fonctionne bien sans aucune erreur. Considérez deux fichiers
a.py
etb.py
.J'ai ajouté cela à
a.py
et cela a fonctionné.a.py:
b.py:
La sortie que j'obtiens est
la source
Ok, je pense que j'ai une solution assez cool. Disons que vous avez un fichier
a
et un fichierb
. Vous avez undef
ouclass
dans le fichierb
que vous souhaitez utiliser dans le modulea
, mais vous avez quelque chose d' autre, que ce soit undef
,class
ou une variable de fichiera
que vous avez besoin dans votre définition ou de la classe dans le fichierb
. Ce que vous pouvez faire est, au bas du fichiera
, après avoir appelé la fonction ou la classe dans le fichiera
qui est nécessaire dans le fichierb
, mais avant d'appeler la fonction ou la classe à partir du fichierb
dont vous avez besoin pour le fichiera
, ditesimport b
ensuite, et voici la partie clé , dans toutes les définitions ou classes du fichierb
qui nécessitent ledef
ou àclass
partir du fichiera
(appelons-leCLASS
), vous ditesfrom a import CLASS
Cela fonctionne car vous pouvez importer un fichier
b
sans que Python n'exécute aucune des instructions d'importation dans le fichierb
, et vous échappez ainsi à toute importation circulaire.Par exemple:
Déposer un:
Fichier b:
Voila.
la source
from a import CLASS
ne saute pas réellement l'exécution de tout le code dans a.py. Voici ce qui se passe réellement: (1) Tout le code dans a.py est exécuté comme un module spécial "__main__". (2) Àimport b
, le code de niveau supérieur dans b.py est exécuté (définissant la classe B), puis le contrôle retourne à "__main__". (3) "__main__" passe éventuellement le contrôle àgo.dostuff()
. (4) lorsque doStuff () vientimport a
, il exécute tout le code dans a.py à nouveau , cette fois que le module « a »; puis il importe l'objet CLASS du nouveau module "a". Donc, en fait, cela fonctionnerait aussi bien si vous l'import a
utilisiez n'importe où dans b.py.