Comment éviter les importations circulaires en Python? [dupliquer]
cette question a déjà une réponse ici:
je sais que la question des importations circulaires en python a été soulevée de nombreuses fois auparavant et j'ai lu ces discussions. Le commentaire qui est fait à plusieurs reprises dans ces discussions est qu'une circulaire à l'importation est un signe d'une mauvaise conception et le code devrait être réorganisée afin d'éviter l'importation circulaire.
Quelqu'un pourrait-il me dire comment éviter une importation circulaire dans cette situation?: J'ai deux classes et je veux que chaque classe dispose d'un constructeur (méthode) qui prend une instance de l'autre classe et retourne une instance de la classe.
plus précisément, une classe est mutable et l'autre est immuable. La classe immuable est nécessaire pour le hachage, comparer et ainsi de suite. La classe mutable est nécessaire pour faire des choses aussi. Ceci est similaire aux sets et frozensets ou aux listes et tuples.
je pourrais mettre les deux définitions de classe dans le même module. Existe-il d'autres suggestions?
un exemple de jouet serait la classe A qui a un attribut qui est une liste et la Classe B qui a un attribut qui est un tuple. Alors la classe A a une méthode qui prend une instance de la Classe B et renvoie une instance de la classe A (par convertir le tuple en liste) et de même la Classe B a une méthode qui prend une instance de la classe A et renvoie une instance de la Classe B (en convertissant la liste en tuple).
3 réponses
n'importe que le module, n'importe pas du module:
prendre en considération a.py
:
import b
class A:
def bar(self):
return b.B()
et b.py
:
import a
class B:
def bar(self):
return a.A()
Cela fonctionne parfaitement bien.
considérons l'exemple suivant de paquet python où a.py
et b.py
dépendent l'un de l'autre:
/package
__init__.py
a.py
b.py
il y a plusieurs façons d'importer un module en python
import package.a # Absolute import
import package.a as a_mod # Absolute import bound to different name
from package import a # Alternate absolute import
import a # Implicit relative import (deprecated, py2 only)
from . import a # Explicit relative import
malheureusement, seules les options 1 et 4 fonctionnent réellement lorsque vous avez des dépendances circulaires (le reste relance toutes ImportError
ou AttributeError
). En général, vous ne devriez pas utiliser la syntaxe 4, car elle ne fonctionne qu'en python2 et risque de se heurter à d'autres modules tiers. Donc vraiment, seule la première syntaxe est garantie de fonctionner. Cependant, vous avez encore plusieurs options pour traiter les dépendances circulaires.
EDIT:
ImportError
etAttributeError
problèmes ne se produisent que dans python 2. En python 3 la machine d'importation a été réécrite et tous de ces déclarations d'importation (à l'exception de 4) fonctionne, même avec les dépendances circulaires.
Use Importations Absolues
il suffit d'utiliser la première syntaxe d'importation ci-dessus. L'inconvénient de cette méthode est que les noms d'importation peuvent obtenir super long pour les grands paquets.
Dans a.py
import package.b
Dans b.py
import package.a
"1519320920 de" Reporter l'importation jusqu'à ce que plus tard
j'ai vu cette méthode utilisée dans beaucoup de paquets, mais cela me semble encore hacky, et je n'aime pas que je ne puisse pas regarder le haut d'un module et voir toutes ses dépendances, je dois aller chercher à travers toutes les fonctions aussi bien.
Dans a.py
def func():
from package import b
Dans b.py
def func():
from package import a
mettre toutes les importations dans un module central
cela fonctionne aussi, mais a le même problème que la première méthode, où tous les appels paquet et submodule obtiennent super long . Il a également deux défauts majeurs -- il force tous les sous-modules à être importés, même si vous utilisez seulement un ou deux, et vous ne pouvez toujours pas regarder l'un des sous-modules et voir rapidement leurs dépendances au sommet, vous devez aller passer en revue les fonctions.
Dans __init__.py
from . import a
from . import b
Dans a.py
import package
def func():
package.b.some_object()
Dans b.py
import package
def func():
package.a.some_object()
donc ce sont vos options (et elles sont toutes un peu nul IMO). Franchement, cela semble être un bogue flagrant dans la machinerie d'importation de python, mais c'est juste mon opinion.
nous faisons une combinaison d'importations absolues et des fonctions pour une meilleure lecture et des chaînes d'accès plus courtes.
- avantage: chaînes d'accès plus courtes par rapport aux importations pures et absolues
- désavantage: un peu plus de frais généraux en raison de l'appel de fonction supplémentaire
main/sub/a.py
import main.sub.b
b_mod = lambda: main.sub.b
class A():
def __init__(self):
print('in class "A":', b_mod().B.__name__)
main/sub/b.py
import main.sub.a
a_mod = lambda: main.sub.a
class B():
def __init__(self):
print('in class "B":', a_mod().A.__name__)