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).

70
demandé sur Sheena 2011-09-07 19:49:53

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.

67
répondu rumpel 2011-09-07 15:55:37

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 et AttributeError 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.

114
répondu Brendan Abel 2016-05-17 01:08:49

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__)
4
répondu Christian Haintz 2017-02-08 13:30:40