Python-Umlaufimport entfernen

8

user.py:

%Vor%

story.py

%Vor%

Wie Sie sehen können, gibt es in diesem Programm einen zirkulären Import, der ein ImportError verursacht. Ich habe gelernt, dass ich die Import-Anweisung in der Methodendefinition verschieben kann, um diesen Fehler zu vermeiden. Aber ich möchte immer noch wissen, gibt es eine Möglichkeit, in diesem Fall Kreisimport zu entfernen, oder, ist es notwendig (für ein gutes Design)?

    
wong2 15.11.2013, 07:49
quelle

4 Antworten

1

Eine andere Möglichkeit, die Kreisförmigkeit zu verringern, besteht darin, den Importstil zu ändern. Ändern Sie from story import Story in import story und beziehen Sie sich dann auf die Klasse als story.Story . Da Sie nur innerhalb einer Methode auf die Klasse verweisen, müssen Sie erst auf die Klasse zugreifen, wenn die Methode aufgerufen wird. Zu diesem Zeitpunkt ist der Import erfolgreich abgeschlossen. (Möglicherweise müssen Sie diese Änderung in einem oder in beiden Modulen vornehmen, je nachdem, welches zuerst importiert wurde.)

Das Design scheint jedoch etwas seltsam zu sein. Ihr Design ist so, dass die Klassen User und Story sehr eng gekoppelt sind - keines kann ohne das andere verwendet werden. In einem solchen Fall wäre es normalerweise sinnvoller, beide im selben Modul zu haben.

    
BrenBarn 15.11.2013 07:53
quelle
1

Die offensichtlichste Lösung in diesem Fall besteht darin, die Abhängigkeit vollständig von der Klasse User zu trennen, indem Sie die Schnittstelle so ändern, dass der Konstruktor Story eine tatsächliche User akzeptiert, nicht eine user_id . Dies führt auch zu einem effizienteren Design: Wenn ein Benutzer beispielsweise viele Storys hat, kann allen Konstruktoren dasselbe Objekt zugewiesen werden.

Ansonsten sollte der Import eines ganzen Moduls (also story und user anstelle der Mitglieder) funktionieren - das zuerst importierte Modul wird zum Zeitpunkt des zweiten Imports leer angezeigt; Es spielt jedoch keine Rolle, da der Inhalt dieser Module nicht im globalen Gültigkeitsbereich verwendet wird.

Dies ist gegenüber dem Import innerhalb einer Methode etwas vorzuziehen. Der Import innerhalb einer Methode hat einen erheblichen Mehraufwand gegenüber nur einer modulübergreifenden Suche ( story.Story ), da sie für jeden Methodenaufruf durchgeführt werden muss. scheint, dass in einem einfachen Fall der Overhead mindestens 30-fach ist.

    
Antti Haapala 15.11.2013 18:15
quelle
1

Es gibt einige dieser python zirkulären Importfragen im Web. 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 Praxis darstellt - den Import in eine Methode zu verschieben.

Abgesehen von Hettingers Autorität sind drei Disclaimer zu gemeinsamen Einwänden notwendig:

  1. Ich habe nie in Java programmiert. Ich versuche nicht Java zu machen.
  2. 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.
  3. Die Kombination von Modulen, die sehr groß sind, kann zu Lesbarkeits- und Wartbarkeitsproblemen führen, die viel schlimmer sind als ein oder zwei rekursive Importe.

Außerdem glaube ich, dass Wartbarkeit und Lesbarkeit dazu führen, dass Importe an der Spitze der Datei gruppiert werden, nur einmal für jeden benötigten Namen vorkommen und dass der Stil from module import name vorzuziehen ist (außer vielleicht für sehr kurze Modulnamen mit vielen) Funktionen, zB gtk ), da es repetitives verbales Durcheinander vermeidet und Abhängigkeiten explizit macht.

Damit werde ich eine vereinfachte Version meines eigenen Anwendungsfalls vorschlagen, der mich hierher gebracht hat, und meine Lösung bereitstellen.

Ich habe zwei Module, die jeweils viele Klassen definieren. surface definiert geometrische Flächen wie Ebenen, Kugeln, Hyperboloide usw. path definiert planare geometrische Figuren wie Linien, Kreishyperbeln usw. Logischerweise sind dies verschiedene Kategorien und Refactoring ist aus Sicht der API-Anforderungen keine Option. 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 Schnitt einer Ebene und einer Kugel ist ein Kreis.

Wenn Sie zum Beispiel in surface.py den direkten Import durchführen, um den Rückgabewert für eine Kreuzungsoperation zu implementieren:

%Vor%

Sie erhalten:

%Vor%

Geometrisch werden Ebenen verwendet, um die Pfade zu definieren, schließlich können sie beliebig in drei (oder mehr) Dimensionen ausgerichtet sein. Das Traceback teilt Ihnen sowohl was passiert und die Lösung.

Ersetzen Sie einfach die Importanweisung in surface.py durch:

%Vor%

Die Abfolge der Operationen in der Rückverfolgung läuft noch. 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 von surface in path geladen. Das Namespace-Parsing von path kann daher abgeschlossen werden, sodass es in surface geladen werden kann und die erste Begegnung mit from path import Line abgeschlossen wird. Daher kann das Namespace-Parsing von surface fortfahren und vervollständigen und zu dem weiterführen, was sonst noch notwendig ist.

Es ist ein einfaches und sehr klares Idiom. Die try: ... except ... -Syntax dokumentiert das zirkuläre Importproblem klar und prägnant und erleichtert zukünftige Wartungsarbeiten. Verwenden Sie es immer, wenn ein Refactor wirklich eine schlechte Idee ist.

    
anonymous_offering 06.12.2017 03:47
quelle
0

Wie BrenBarn sagte, ist die naheliegendste Lösung, Benutzer und Story im selben Modul zu halten, was sehr sinnvoll ist, wenn der User etwas über Story wissen soll. Nun, wenn Sie wirklich brauchen, um sie in verschiedenen Modulen zu haben, können Sie auch den Benutzer in story.py anpassen, um die Methode get_stories hinzuzufügen. Es ist ein Lesbarkeits- / Entkopplungs-Trade-Off ...

    
bruno desthuilliers 15.11.2013 11:34
quelle