Zirkuläre Abhängigkeit in Python

71

Ich habe zwei Dateien, node.pyund path.py, die zwei Klassen zu definieren, Nodeund Pathsind.

Bis heute hat die Definition für Pathdas NodeObjekt referenziert , und deshalb hatte ich getan

from node.py import *

in der path.pyDatei.

Ab heute habe ich jedoch eine neue Methode erstellt Node, die auf das PathObjekt verweist .

Ich hatte Probleme beim Importieren path.py: Ich habe es versucht, und als das Programm ausgeführt und die verwendete PathMethode aufgerufen wurde , Nodetrat eine Ausnahme auf Node, weil es nicht definiert wurde.

Was mache ich?

Ram Rachum
quelle
3
Versuchen Sie, eine Klasse pro Datei zu haben? Deshalb funktioniert das selten gut.
S.Lott
4
Stimmen Sie mit S.Lott überein. Python ist nicht Java. Sie benötigen keine Klasse pro Datei.
Daniel Roseman
42
Ein paar Leute haben gesagt "Sie brauchen keine Klasse pro Datei" und Worte mit dem Effekt "Versuchen Sie nicht, Java zu sein". OK - aber es ist falsch. Klassendefinitionen können sehr groß werden, und wenn sie in derselben Datei gebündelt werden, kann dies zu einer sehr großen, nicht lesbaren Datei führen. In einem Programm, an dem ich mit 8 voneinander abhängigen Klassen arbeite, von denen jede mehrere hundert Zeilen lang ist, sehe ich keinen Vorteil darin, sie in derselben Datei zu halten, und einen erheblichen Vorteil darin, sie getrennt zu halten.
Sfkleach
1
auch nur als Referenz scheint es, dass zirkuläre Importe auf Python 3.5 (und wahrscheinlich darüber hinaus) erlaubt sind, aber nicht auf 3.4 (und wahrscheinlich unten).
Charlie Parker

Antworten:

114

Das Importieren von Python-Modulen ist ein großartiger Artikel, in dem zirkuläre Importe in Python erläutert werden.

Der einfachste Weg, dies zu beheben, besteht darin, den Pfadimport an das Ende des Knotenmoduls zu verschieben.

Nadia Alramli
quelle
Okay, aber die Sache ist, ich habe zwei andere Module tree.pyund block.pyin diesem Paket, die erfordern node.pyund von benötigt werden path.py. Soll ich sie alle in eine Datei packen? Ich mochte ein Modul pro Klasse.
Ram Rachum
2
Hast du meinen Vorschlag ausprobiert? Es wird wahrscheinlich funktionieren. Verschieben Sie den Import einfach an das Ende der Datei. Ich rate Ihnen, den Artikel zu lesen, um zu verstehen, warum dies geschieht.
Nadia Alramli
12
Gibt es übrigens einen anderen Grund, als dass Sie es "mögen", dass Sie eine Klasse pro Modul wollen? Die wenigen Male, die ich diese Präferenz gesehen habe, war, weil es Java-ähnlich ist.
Zot
4
Der Link zum Importieren von Python-Modulen ist unterbrochen.
Ben2209
26

Ein anderer Ansatz besteht darin, eines der beiden Module nur in der Funktion zu importieren, in der Sie es in dem anderen benötigen. Sicher, dies funktioniert am besten, wenn Sie es nur in einer oder wenigen Funktionen benötigen:

# in node.py 
from path import Path
class Node 
    ...

# in path.py
class Path
  def method_needs_node(): 
    from node import Node
    n = Node()
    ...
mircealungu
quelle
3

Ich ziehe es vor, eine zirkuläre Abhängigkeit zu brechen, indem ich eine der Abhängigkeiten im Konstruktor der anderen abhängigen Klasse deklariere. Meiner Ansicht nach hält dies den Code übersichtlicher und ermöglicht einen einfachen Zugriff auf alle Methoden, die die Abhängigkeit erfordern.

In meinem Fall habe ich also einen CustomerService und einen UserService, die voneinander abhängig sind. Ich unterbreche die zirkuläre Abhängigkeit wie folgt:

class UserService:

    def __init__(self):
        # Declared in constructor to avoid circular dependency
        from server.portal.services.admin.customer_service import CustomerService
        self.customer_service = CustomerService()

    def create_user(self, customer_id: int) -> User:
        # Now easy to access the dependency from any method
        customer = self.customer_service.get_by_id(customer_id)
Iain Hunter
quelle
2

Sie können nicht importieren müssen Pathin node.pyfür in Ordnung Pathund Nodezu nutzen voneinander.

# in __init__.py  (The order of imports should not matter.)
from .node import Node
from .path import Path

# in path.py 
from . import Node
class Path
  ...

  def return_something_pathy(self): 
    ...

# in node.py
class Node
  def __init__(self, path): 
    self.path = path
    ...

  def a_node_method():
    print(self.path.return_something_pathy())

Fügen Sie Typhinweise hinzu, um zu verdeutlichen, Nodedass davon Gebrauch gemacht Pathwird. Ab Python 3.7 ist eine Funktion verfügbar, die Vorwärtsreferenzen in Typanmerkungen unterstützt, wie in PEP 563 beschrieben .

# in node.py  (Now with type hinting.)
from __future__ import annotations

class Node
  def __init__(self, path: Path): 
    self.path = path
    ...

  def a_node_method():
    print(self.path.return_something_pathy())

Ich bin auf eine weitere Lösung gestoßen, um Sie aus einem kreisförmigen Importloch in Python herauszuholen, ist ein großartiger Blog-Beitrag, der mir dies beigebracht hat.

Bryan Roach
quelle