Angenommen, ich habe die folgende Verzeichnisstruktur:
a\
__init__.py
b\
__init__.py
c\
__init__.py
c_file.py
d\
__init__.py
d_file.py
In den a
Paketen __init__.py
wird das c
Paket importiert. Aber c_file.py
Importe a.b.d
.
Das Programm schlägt fehl und sagt, dass b
es beim c_file.py
Importieren nicht vorhanden ist a.b.d
. (Und es existiert wirklich nicht, weil wir gerade dabei waren, es zu importieren.)
Wie kann dieses Problem behoben werden?
python
dependencies
circular-dependency
python-import
Ram Rachum
quelle
quelle
Antworten:
Wenn a von c und c von a abhängt, sind sie dann nicht tatsächlich dieselbe Einheit?
Sie sollten wirklich untersuchen, warum Sie a und c in zwei Pakete aufgeteilt haben, da Sie entweder Code haben, den Sie in ein anderes Paket aufteilen sollten (damit beide von diesem neuen Paket abhängen, aber nicht voneinander), oder Sie sollten sie zusammenführen in ein Paket.
quelle
Sie können den Import verschieben, zum Beispiel in
a/__init__.py
:def my_function(): from a.b.c import Blah return Blah()
Verschieben Sie den Import, bis er wirklich benötigt wird. Ich würde mir jedoch auch meine Paketdefinitionen / -verwendungen genauer ansehen, da eine zyklische Abhängigkeit wie die oben genannte auf ein Entwurfsproblem hinweisen könnte.
quelle
sys.modules
). Sobald ein Modul geladen wurde, wird es nicht mehr geladen. Der Code kann eine Namenssuche bei jedem Aufruf von beinhaltenmy_function
, aber auch Code, der Symbole über qualifizierte Namen (z. B.import foo; foo.frobnicate()
) referenziertIch habe mich das ein paar Mal gefragt (normalerweise beim Umgang mit Modellen, die voneinander wissen müssen). Die einfache Lösung besteht darin, nur das gesamte Modul zu importieren und dann auf das zu verweisen, was Sie benötigen.
Also anstatt zu tun
from models import Student
in einem und
from models import Classroom
in der anderen einfach tun
import models
Rufen Sie in einem von ihnen models.Classroom an, wenn Sie es brauchen.
quelle
Zirkuläre Abhängigkeiten aufgrund von Typhinweisen
Mit Typhinweisen gibt es mehr Möglichkeiten, zirkuläre Importe zu erstellen. Glücklicherweise gibt es eine Lösung mit der speziellen Konstante :
typing.TYPE_CHECKING
.Das folgende Beispiel definiert eine
Vertex
Klasse und eineEdge
Klasse. Eine Kante wird durch zwei Scheitelpunkte definiert, und ein Scheitelpunkt verwaltet eine Liste der benachbarten Kanten, zu denen er gehört.Ohne Typhinweise kein Fehler
Datei: vertex.py
class Vertex: def __init__(self, label): self.label = label self.adjacency_list = []
Datei: edge.py
class Edge: def __init__(self, v1, v2): self.v1 = v1 self.v2 = v2
Typ Hinweise Ursache ImportError
Datei: vertex.py
from typing import List from edge import Edge class Vertex: def __init__(self, label: str): self.label = label self.adjacency_list: List[Edge] = []
Datei: edge.py
from vertex import Vertex class Edge: def __init__(self, v1: Vertex, v2: Vertex): self.v1 = v1 self.v2 = v2
Lösung mit TYPE_CHECKING
Datei: vertex.py
from typing import List, TYPE_CHECKING if TYPE_CHECKING: from edge import Edge class Vertex: def __init__(self, label: str): self.label = label self.adjacency_list: List['Edge'] = []
Datei: edge.py
from typing import TYPE_CHECKING if TYPE_CHECKING: from vertex import Vertex class Edge: def __init__(self, v1: 'Vertex', v2: 'Vertex'): self.v1 = v1 self.v2 = v2
Zitierte oder nicht zitierte Typhinweise
In Versionen von Python vor 3.10 müssen bedingt importierte Typen in Anführungszeichen gesetzt werden, sodass sie "Weiterleitungsreferenzen" sind, wodurch sie vor der Interpreter-Laufzeit verborgen werden.
In Python 3.7, 3.8 und 3.9 besteht eine Problemumgehung darin, den folgenden speziellen Import zu verwenden.
from __future__ import annotations
Dies ermöglicht die Verwendung von nicht zitierten Typhinweisen in Kombination mit bedingten Importen.
Python 3.10 (Siehe PEP 563 - Verschiebung der Auswertung von Anmerkungen )
quelle
Das Problem ist, dass beim Ausführen aus einem Verzeichnis standardmäßig nur die Pakete als Unterverzeichnisse als Kandidatenimporte sichtbar sind, sodass Sie abd nicht importieren können. Sie können jedoch bd importieren, da b ein Unterpaket von a ist.
Wenn Sie abd wirklich importieren möchten,
c/__init__.py
können Sie dies erreichen, indem Sie den Systempfad in ein Verzeichnis über a ändern und den Import ina/__init__.py
abc importierenDu
a/__init__.py
solltest so 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 zusätzliche Schwierigkeit tritt auf, wenn Sie Module in c als Skripte ausführen möchten. Hier existieren die Pakete a und b nicht. Sie können das
__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 verwenden zu können. Ich bezweifle, dass es eine gute Praxis ist, es zu importieren,__init__.py
aber es hat für meine Anwendungsfälle gearbeitet.quelle
Ich schlage das folgende Muster vor. Wenn Sie es verwenden, funktionieren die automatische Vervollständigung und die Eingabe von Hinweisen ordnungsgemäß.
cyclic_import_a.py
import playground.cyclic_import_b class A(object): def __init__(self): pass def print_a(self): print('a') if __name__ == '__main__': a = A() a.print_a() b = playground.cyclic_import_b.B(a) b.print_b()
cyclic_import_b.py
import playground.cyclic_import_a class B(object): def __init__(self, a): self.a: playground.cyclic_import_a.A = a def print_b(self): print('b1-----------------') self.a.print_a() print('b2-----------------')
Mit dieser Syntax können Sie die Klassen A und B nicht importieren
from playgroud.cyclic_import_a import A from playground.cyclic_import_b import B
Sie können den Typ des Parameters a in der Methode Klasse B __ init __ nicht deklarieren, aber Sie können ihn folgendermaßen "umwandeln":
def __init__(self, a): self.a: playground.cyclic_import_a.A = a
quelle
Eine andere Lösung besteht darin, einen Proxy für die Datei d_file zu verwenden.
Angenommen, Sie möchten die bla-Klasse mit der Datei c_file teilen. Die d_file enthält also:
class blah: def __init__(self): print("blah")
Folgendes geben Sie in c_file.py ein:
# 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 a's 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()
quelle