Es gibt eine Reihe von diesen python
kreisförmige Import Fragen im Internet. Ich habe mich dazu entschieden, zu diesem Thread beizutragen, weil die Anfrage einen Kommentar von Ray Hettinger enthält, der den Anwendungsfall eines zirkulären Imports legitimiert, aber eine Lösung empfiehlt, die meiner Meinung nach keine besonders gute Übung ist - den Import in eine Methode zu verschieben.
Neben Hettinger Autorität, sind drei Haftungsausschluss zu gemeinsamen Einwand notwendig:
- Ich habe noch nie in Java programmiert. Ich versuche nicht Java zu machen.
- Refactoring ist nicht immer nützlich oder effektiv.Die logische API gibt manchmal eine Struktur vor, die rekursive Importverweise unvermeidbar macht. Denken Sie daran, Code existiert für Benutzer, nicht für Programmierer.
- Das Kombinieren von Modulen, die ziemlich groß sind, kann Probleme mit der Lesbarkeit und Wartbarkeit verursachen, die viel schlimmer sein können als ein oder zwei rekursive Importe.
Darüber hinaus glaube ich, Wartbarkeit und Lesbarkeit schreibt vor, dass die Einfuhren an der Spitze der Datei gruppiert werden, treten nur einmal für jeden benötigten Namen, und dass die from module import name
Stil bevorzugt, (außer vielleicht für sehr kurze Modulnamen mit viele Funktionen, zB gtk
), da es sich wiederholende verbale Unordnung vermeidet und Abhängigkeiten explizit macht.
Damit werde ich eine vereinfachte Version meines eigenen Anwendungsfalles, der mich hierher gebracht hat, vorschlagen und meine Lösung anbieten.
Ich habe zwei Module, die jeweils viele Klassen definieren. surface
definiert geometrische Oberflächen wie Ebenen, Kugeln, Hyperboloide usw. path
definiert planare geometrische Figuren wie Linien, Kreise, Hyperbeln usw. Logischerweise sind dies verschiedene Kategorien und Refactoring ist keine Option aus Sicht der API-Anforderungen. Dennoch sind diese beiden Kategorien intim.
Eine nützliche Operation schneidet zwei Oberflächen, zum Beispiel ist der Schnittpunkt zweier Ebenen eine Linie, oder der Schnittpunkt einer Ebene und einer Kugel ist ein Kreis.
Wenn zum Beispiel in surface.py
Sie geradeaus Import tun musste, um den Rückgabewert für einen Schnittbetrieb implementieren:
from path import Line
Sie erhalten:
Traceback (most recent call last):
File "surface.py", line 62, in <module>
from path import Line
File ".../path.py", line 25, in <module>
from surface import Plane
File ".../surface.py", line 62, in <module>
from path import Line
ImportError: cannot import name Line
Geometrisch sind Flugzeuge verwendet Definieren Sie die Pfade, schließlich können sie beliebig in drei (oder mehr) Dimensionen orientiert sein. Das Traceback teilt Ihnen mit, was passiert und welche Lösung es gibt.
Ersetzen Sie einfach die Import-Anweisung in surface.py
mit:
try: from path import Line
except ImportError: pass # skip circular import second pass
die Folge von Operationen in der Spur zurück noch geschieht. Es ist nur so, dass wir beim zweiten Mal den Importfehler ignorieren. Dies spielt keine Rolle, da Line
nicht auf Modulebene verwendet wird. Daher wird der erforderliche Namespace surface
in path
geladen. Das Namespace-Parsing von path
kann daher abgeschlossen werden, wodurch es möglich ist, es in surface
zu laden und die erste Begegnung mit from path import Line
abzuschließen. Daher kann das Namespace-Parsing von surface
fortfahren und vervollständigen, was auch immer sonst notwendig ist.
Es ist ein einfaches und sehr klares Idiom. Die try: ... except ...
Syntax dokumentiert klar und prägnant das zirkuläre Importproblem und erleichtert zukünftige Wartungsarbeiten. Verwenden Sie es immer, wenn ein Refactor wirklich eine schlechte Idee ist.
Es ist nicht notwendig, den kreisförmigen Import für gutes Design zu entfernen. Das Verschieben des Imports in eine Methodendefinition ist eine sinnvolle Möglichkeit, einen Import zu verschieben. –