Dépendance à l'importation circulaire en Python

disons que j'ai la structure de répertoire suivante:

a
    __init__.py
    b
        __init__.py
        c
            __init__.py
            c_file.py
        d
            __init__.py
            d_file.py

dans le paquet a __init__.py , le paquet c est importé. Mais c_file.py importations a.b.d .

le programme échoue, disant b n'existe pas quand c_file.py essaie d'importer a.b.d . (Et ça n'existe pas vraiment, parce qu'on était en train de l'importer.)

Comment ce problème remédier?

65
demandé sur CharlesB 2009-10-12 23:19:30

5 réponses

Si a dépend de c et c dépend de a, ne sont-ils pas en fait la même unité alors?

vous devriez vraiment examiner pourquoi vous avez divisé a et c en deux paquets, parce que soit vous avez un code que vous devriez séparer dans un autre paquet (pour les faire dépendre tous les deux de ce nouveau paquet, mais pas l'un de l'autre), ou vous devriez les fusionner dans un paquet.

55
répondu Lasse Vågsæther Karlsen 2009-10-12 19:33:28

vous pouvez différer l'importation, par exemple dans a/__init__.py :

def my_function():
    from a.b.c import Blah
    return Blah()

qui est, de reporter l'importation jusqu'à ce qu'il est vraiment nécessaire. Cependant, je voudrais aussi examiner de près les définitions/utilisations de mes paquets, car une dépendance cyclique comme celle que j'ai mentionnée pourrait indiquer un problème de conception.

145
répondu Dirk 2009-10-12 19:27:53

je me suis posé la question à quelques reprises (habituellement en traitant avec des modèles qui ont besoin de savoir les uns sur les autres). La solution simple est d'importer le module entier, puis de faire référence à la chose dont vous avez besoin.

ainsi au lieu de faire

from models import Student

en un, et

from models import Classroom

dans l'autre, il suffit de faire

import models

dans l'un d'eux, puis les modèles.Salle de classe quand vous en avez besoin.

22
répondu zachaysan 2013-10-04 16:18:32

le problème est que lors de l'exécution à partir d'un répertoire, par défaut seuls les paquets qui sont des sous-répertoires sont visibles lors de l'importation de candidats, donc vous ne pouvez pas importer un.B. D. Vous pouvez cependant importer B. D. puisque b est un sous-paquet de A.

si vous voulez vraiment importer un.B. d dans c/__init__.py vous pouvez accomplir ceci en changeant le chemin du système pour être un répertoire au-dessus de a et en changeant l'import dans a/__init__.py pour être import A. B. C.

votre a/__init__.py devrait ressembler à ceci:

import sys
import os
# set sytem path to be directory above so that a can be a 
# package namespace
DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) 
sys.path.insert(0,DIRECTORY_SCRIPT+"/..")
import a.b.c

une difficulté supplémentaire se pose lorsque vous voulez exécuter des modules dans des scripts C. Ici, les paquets a et b n'existent pas. Vous pouvez hacker le __int__.py dans le répertoire c pour pointer le système.chemin vers le répertoire de haut niveau et ensuite importer __init__ dans n'importe quel module à l'intérieur de c pour être en mesure d'utiliser le chemin complet pour importer A. B. D. Je doute que ce soit une bonne pratique d'importer __init__.py mais cela a fonctionné pour mes cas d'utilisation.

0
répondu John Gilmer 2015-08-30 23:33:12

une autre solution consiste à utiliser un proxy pour le fichier d_file.

par exemple, disons que vous voulez partager la classe bla avec c_file. Le fichier d_file contient donc:

class blah:
    def __init__(self):
        print("blah")

voici ce que vous entrez dans c_file.py:

# do not import the d_file ! 
# instead, use a place holder for the proxy of d_file
# it will be set by a's __init__.py after imports are done
d_file = None 

def c_blah(): # a function that calls d_file's blah
    d_file.blah()

et dans a init .PY:

from b.c import c_file
from b.d import d_file

class Proxy(object): # module proxy
    pass
d_file_proxy = Proxy()
# now you need to explicitly list the class(es) exposed by d_file
d_file_proxy.blah = d_file.blah 
# finally, share the proxy with c_file
c_file.d_file = d_file_proxy

# c_file is now able to call d_file.blah
c_file.c_blah() 
-3
répondu user474491 2012-07-17 21:20:20