2009-10-12 9 views
60

Lassen Sie uns sagen, ich habe die folgende Verzeichnisstruktur:Rundimportabhängigkeit in Python

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

Im a Pakets __init__.py, das c Paket wird importiert. Aber c_file.py Importe a.b.d.

Das Programm schlägt fehl, sagt b ist nicht vorhanden, wenn c_file.py versucht, a.b.d zu importieren. (Und es existiert wirklich nicht, weil wir gerade dabei waren, es zu importieren.)

Wie kann dieses Problem behoben werden?

+1

Vielleicht könnten Sie relative Importe versuchen? http://stackoverflow.com/questions/72852/how-to-do-relative-imports-in-python – eremzeit

+1

dies helfen kann https://ncoghlan_devs-python-notes.readthedocs.org/en /latest/python_concepts/import_traps.html – maazza

+0

auch nur als eine Referenz, es scheint, kreisförmige Importe sind erlaubt auf Python 3.5 (und wahrscheinlich darüber hinaus), aber nicht 3.4 (und wahrscheinlich unten). –

Antwort

53

Wenn ein hängt von c und c hängt von a ab, sind sie nicht eigentlich die gleiche Einheit dann?

Sie sollten wirklich untersuchen, warum Sie a und c in zwei Pakete aufgeteilt haben, weil entweder Sie Code haben, den Sie in ein anderes Paket abspalten sollten (damit beide von diesem neuen Paket abhängen, aber nicht), oder Sie sollten sie in ein Paket zusammenführen.

+83

Ja, sie könnten als das gleiche Paket betrachtet werden. Aber wenn dies zu einer riesigen Datei führt, dann ist das unpraktisch. Ich stimme zu, dass zyklische Abhängigkeiten häufig bedeuten, dass das Design erneut durchdacht werden sollte. Aber da gibt es einige Entwurfsmuster, wo es angebracht ist (und wo das Zusammenführen der Dateien zu einer riesigen Datei führen würde), also denke ich, es ist dogmatisch zu sagen, dass die Pakete entweder kombiniert werden sollten oder das Design neu bewertet werden sollte. –

140

können Sie den Import verschieben, zum Beispiel in a/__init__.py:

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

Das heißt, die Einfuhr aufzuschieben, bis es wirklich benötigt wird. Ich würde jedoch auch meine Paketdefinitionen/-verwendungen genau unter die Lupe nehmen, da eine zyklische Abhängigkeit wie die, auf die hingewiesen wurde, auf ein Entwurfsproblem hinweisen könnte.

+5

Froh, dass Sie darauf hingewiesen haben, das hat perfekt für mich funktioniert. Ich habe sorgfältig über mein Design nachgedacht und gedacht, dass es in diesem Fall gut ist. –

+3

Manchmal sind zirkuläre Referenzen wirklich unvermeidbar. Dies ist der einzige Ansatz, der unter diesen Umständen für mich funktioniert. –

+1

Würde das nicht viel Overhead bei jedem Anruf von foo hinzufügen? –

-3

Eine andere Lösung besteht darin, einen Proxy für die d_Datei zu verwenden.

Nehmen wir zum Beispiel an, dass Sie die Blah-Klasse mit der c_Datei teilen möchten. Die d_file enthält somit:

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

Hier ist, was Sie in c_file.py eingeben:

# 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() 

Und in einem ist 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() 
+11

Modifizieren von globalen Modulattributen in einer anderen Datei wie diesem wird schnell zu einem Albtraum führen – Antimony

19

Ich habe mich gefragt, diese ein paar Mal (in der Regel im Umgang mit Modellen, die über einander wissen müssen). Die einfache Lösung besteht darin, das gesamte Modul zu importieren und dann auf das zu verweisen, was Sie benötigen.

Also statt

from models import Student 

in einem zu tun, und

from models import Classroom 

in der anderen, tun nur

import models 

in einer von ihnen, dann rufen models.Classroom wenn du brauchst es.

+1

Elegant und brilliant !! – nish

0

Das Problem ist, dass beim Ausführen von einem Verzeichnis standardmäßig nur die Pakete, die Unterverzeichnisse sind als Importkandidaten sichtbar sind, so dass Sie a.b.d nicht importieren können. Sie können jedoch b.d. da b ist ein Unterpaket von a.

Wenn Sie wirklich a.b.d in c/__init__.py importieren möchten, können Sie dies erreichen, indem Sie den Systempfad auf ein Verzeichnis über a ändern und den Import in a/__init__.py ändern, um a.b.c. importiert zu werden.

Ihre a/__init__.py sollte wie folgt aussehen:

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 

Eine weitere Schwierigkeit ergibt sich, wenn Sie Module in c als Skripte ausgeführt werden soll. Hier existieren die Pakete a und b nicht. Sie können die __int__.py im c-Verzeichnis hacken, um den sys.path auf das Verzeichnis der obersten Ebene zu verweisen, und dann __init__ in alle Module in c importieren, um den vollständigen Pfad zum Importieren von a.b.d zu verwenden. Ich bezweifle, dass es eine gute Praxis ist, __init__.py zu importieren, aber es hat für meine Anwendungsfälle funktioniert.