Ich möchte eine Funktion aus einer anderen Datei im selben Verzeichnis importieren.
Manchmal funktioniert es bei mir mit, from .mymodule import myfunction
aber manchmal bekomme ich ein:
SystemError: Parent module '' not loaded, cannot perform relative import
Manchmal funktioniert es mit from mymodule import myfunction
, aber manchmal bekomme ich auch:
SystemError: Parent module '' not loaded, cannot perform relative import
Ich verstehe die Logik hier nicht und konnte keine Erklärung finden. Das sieht völlig zufällig aus.
Könnte mir jemand erklären, was die Logik dahinter ist?
python
python-3.x
python-import
John Smith Optional
quelle
quelle
Antworten:
Es ist durchaus üblich, ein solches Layout zu haben ...
... mit so einem
mymodule.py
......
myothermodule.py
so etwas ...... und
main.py
so ...... was gut funktioniert, wenn Sie ausführen
main.py
odermypackage/mymodule.py
, abermypackage/myothermodule.py
aufgrund des relativen Imports fehlschlägt ...Die Art und Weise, wie Sie es ausführen sollen, ist ...
... aber es ist etwas ausführlich und passt nicht gut zu einer Shebang-Linie wie
#!/usr/bin/env python3
.Die einfachste Lösung für diesen Fall, vorausgesetzt, der Name
mymodule
ist global eindeutig, besteht darin, die Verwendung relativer Importe zu vermeiden und nur ...... obwohl, wenn es nicht eindeutig ist oder Ihre Paketstruktur komplexer ist, Sie das Verzeichnis, in dem sich Ihr Paketverzeichnis befindet, einschließen müssen
PYTHONPATH
, und dies folgendermaßen tun ...... oder wenn Sie möchten, dass es "out of the box" funktioniert, können Sie den
PYTHONPATH
In-Code zuerst mit diesem Frob ...Es ist eine Art Schmerz, aber es gibt einen Hinweis darauf, warum in einer E-Mail, die von einem bestimmten Guido van Rossum geschrieben wurde ...
Ob das Ausführen von Skripten in einem Paket ein Antimuster ist oder nicht, ist subjektiv, aber ich persönlich finde es in einem Paket, das einige benutzerdefinierte wxPython-Widgets enthält, sehr nützlich, sodass ich das Skript für jede der Quelldateien ausführen kann, um nur eine zu
wx.Frame
enthalten dieses Widget zu Testzwecken.quelle
os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
ob Sie sicher wären, dass Ihr Modul immer einen richtigen hat, denfile
Sie auch verwenden könntenos.path.realpath(os.path.dirname(__file__))
.sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
...which I've always seen as an antipattern.
Ich verstehe nicht, wie es ein Anti-Muster ist ... Es scheint sehr praktisch zu sein, relative Importe einfach intuitiv arbeiten zu lassen. Ich möchte nur Dinge importieren können, von denen ich weiß, dass sie sich im selben Verzeichnis befinden. Ich frage mich, was seine Argumentation warErläuterung
Aus PEP 328
Irgendwann kollidierte PEP 338 mit PEP 328 :
und um das Problem zu beheben , führte PEP 366 die Variable der obersten Ebene ein
__package__
:(Hervorhebung von mir)
Wenn dies der Fall
__name__
ist'__main__'
, wird eine__name__.rpartition('.')[0]
leere Zeichenfolge zurückgegeben. Aus diesem Grund enthält die Fehlerbeschreibung ein leeres Zeichenfolgenliteral:Der relevante Teil der CPython-
PyImport_ImportModuleLevelObject
Funktion :CPython löst diese Ausnahme aus, wenn es
package
(den Namen des Pakets) ininterp->modules
(zugänglich alssys.modules
) nicht finden konnte. Dasys.modules
es sich um ein "Wörterbuch handelt, das Modulnamen bereits geladenen Modulen zuordnet" , ist jetzt klar, dass das übergeordnete Modul explizit absolut importiert werden muss, bevor ein relativer Import durchgeführt wird .Hinweis: Der Patch aus der Ausgabe 18018 hat einen weiteren
if
Block hinzugefügt, der vor dem obigen Codeausgeführt wird:Wenn
package
(wie oben) eine leere Zeichenfolge ist, wird die Fehlermeldung angezeigtDies wird jedoch nur in Python 3.6 oder höher angezeigt.
Lösung 1: Führen Sie Ihr Skript mit -m aus
Betrachten Sie ein Verzeichnis (das ein Python- Paket ist ):
Alle Dateien im Paket beginnen mit denselben 2 Codezeilen:
Ich füge diese beiden Zeilen nur hinzu , um die Reihenfolge der Operationen zu verdeutlichen. Wir können sie vollständig ignorieren, da sie die Ausführung nicht beeinflussen.
__init__.py und module.py enthalten nur diese beiden Zeilen (dh sie sind effektiv leer).
standalone.py versucht zusätzlich, module.py über den relativen Import zu importieren:
Wir sind uns bewusst, dass dies
/path/to/python/interpreter package/standalone.py
fehlschlagen wird. Wir können das Modul jedoch mit der-m
Befehlszeilenoption ausführen , die "nachsys.path
dem benannten Modul sucht und dessen Inhalt als__main__
Modul ausführt " :-m
erledigt den ganzen Import für dich und setzt automatisch__package__
, aber das kannst du selbst in derLösung 2: Stellen Sie __package__ manuell ein
Bitte behandeln Sie es eher als Proof of Concept als als tatsächliche Lösung. Es ist nicht gut für die Verwendung in realem Code geeignet.
PEP 366 bietet eine Problemumgehung für dieses Problem, ist jedoch unvollständig, da die Einstellung
__package__
allein nicht ausreicht. Sie müssen mindestens N vorhergehende Pakete in die Modulhierarchie importieren , wobei N die Anzahl der übergeordneten Verzeichnisse (relativ zum Verzeichnis des Skripts) ist, die nach dem zu importierenden Modul durchsucht werden.Somit,
Fügen Sie das übergeordnete Verzeichnis des N-ten Vorgängers des aktuellen Moduls hinzu
sys.path
Entfernen Sie das Verzeichnis der aktuellen Datei aus
sys.path
Importieren Sie das übergeordnete Modul des aktuellen Moduls unter Verwendung seines vollständig qualifizierten Namens
Stellen Sie
__package__
den vollständig qualifizierten Namen von 2 einFühren Sie den relativen Import durch
Ich werde Dateien aus der Lösung Nr. 1 ausleihen und weitere Unterpakete hinzufügen:
Dieses Mal standalone.py importiert module.py aus dem Paket - Paket mit dem folgenden relativen Import
Wir müssen dieser Zeile den Boilerplate-Code voranstellen, damit er funktioniert.
Es ermöglicht uns, standalone.py nach Dateiname auszuführen :
Eine allgemeinere Lösung, die in eine Funktion eingeschlossen ist, finden Sie hier . Anwendungsbeispiel:
Lösung 3: Verwenden Sie absolute Importe und Setuptools
Die Schritte sind -
Ersetzen Sie explizite relative Importe durch äquivalente absolute Importe
Installieren
package
, um es importierbar zu machenBeispielsweise kann die Verzeichnisstruktur wie folgt sein
wo setup.py ist
Der Rest der Dateien wurde aus der Lösung Nr. 1 ausgeliehen .
Durch die Installation können Sie das Paket unabhängig von Ihrem Arbeitsverzeichnis importieren (vorausgesetzt, es treten keine Namensprobleme auf).
Wir können standalone.py ändern , um diesen Vorteil zu nutzen (Schritt 1):
Ändern Sie Ihr Arbeitsverzeichnis in
project
und führen Sie es aus/path/to/python/interpreter setup.py install --user
(--user
installiert das Paket in Ihrem Site-Packages-Verzeichnis ) (Schritt 2):Lassen Sie uns überprüfen, ob es jetzt möglich ist, standalone.py als Skript auszuführen :
Hinweis : Wenn Sie sich für diesen Weg entscheiden, sollten Sie virtuelle Umgebungen verwenden , um Pakete isoliert zu installieren.
Lösung 4: Verwenden Sie absolute Importe und etwas Boilerplate-Code
Ehrlich gesagt ist die Installation nicht erforderlich - Sie können Ihrem Skript Boilerplate-Code hinzufügen, damit absolute Importe funktionieren.
Ich werde Dateien von Lösung 1 ausleihen und standalone.py ändern :
Fügen Sie das übergeordnete Verzeichnis des Pakets hinzu ,
sys.path
bevor Sie versuchen, mithilfe absoluter Importe etwas aus dem Paket zu importieren:Ersetzen Sie den relativen Import durch den absoluten Import:
standalone.py läuft ohne Probleme:
Ich bin der Meinung, dass ich Sie warnen sollte: Versuchen Sie, dies nicht zu tun, insbesondere wenn Ihr Projekt eine komplexe Struktur aufweist.
Als Randnotiz empfiehlt PEP 8 die Verwendung absoluter Importe, gibt jedoch an, dass in einigen Szenarien explizite relative Importe akzeptabel sind:
quelle
__package__
manuell festzulegen, ob der Name lautet__main__
, um das Problem zu lösen?imp
Modul laden und entsprechend einstellen,__package__
aber das Ergebnis ist eindeutig ein Anti-Pattern.AttributeError: 'PosixPath' object has no attribute 'path'
.Fügen Sie dies in die Datei __init__.py Ihres Pakets ein :
Angenommen, Ihr Paket sieht folgendermaßen aus:
Verwenden Sie jetzt regelmäßige Importe in Ihrem Paket, wie:
Dies funktioniert sowohl in Python 2 als auch in Python 3.
quelle
__init__.py
Grunde alle relativen Importfehler behoben.sys.path
weil ich befürchte, dass dies Auswirkungen auf anderen Code haben könnte. (Teilweise liegt das daran, dass ich nicht weiß, wieIch bin auf dieses Problem gestoßen. Eine Hack-Problemumgehung wird wie folgt über einen if / else-Block importiert:
quelle
except:
ist schlecht. verwendenexcept ImportError:
statt!SystemError
hier. (Py 3.4)if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
.Hoffentlich ist dies für jemanden da draußen von Wert - ich habe ein halbes Dutzend Stackoverflow-Posts durchgesehen, um relative Importe zu ermitteln, die denen ähneln, die hier oben veröffentlicht wurden. Ich habe alles wie vorgeschlagen eingerichtet, aber ich habe immer noch geschlagen
ModuleNotFoundError: No module named 'my_module_name'
Da ich mich nur lokal entwickelte und herumspielte, hatte ich keine
setup.py
Datei erstellt / ausgeführt . Ich hatte anscheinend auch meine nicht eingestelltPYTHONPATH
.Ich stellte fest, dass ich mein Modul nicht finden konnte, als ich meinen Code so ausführte, wie ich es war, als sich die Tests im selben Verzeichnis wie das Modul befanden:
Als ich jedoch den Pfad explizit spezifizierte, begannen die Dinge zu funktionieren:
Für den Fall, dass jemand ein paar Vorschläge ausprobiert hat, glaubt, dass sein Code korrekt strukturiert ist und sich dennoch in einer ähnlichen Situation befindet wie ich, versuchen Sie eine der folgenden Möglichkeiten, wenn Sie das aktuelle Verzeichnis nicht in Ihren PYTHONPATH exportieren:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, erstellen Sie einesetup.py
Datei mit Inhalten wie den folgenden und führen Sie sie auspython setup.py development
, um dem Pfad Pakete hinzuzufügen:quelle
Ich musste python3 aus dem Hauptprojektverzeichnis ausführen, damit es funktioniert.
Zum Beispiel, wenn das Projekt die folgende Struktur hat:
Lösung
Ich würde python3 im Ordner project_demo / ausführen und dann a ausführen
quelle
Um dieses Problem zu vermeiden, habe ich eine Lösung mit dem Umpackpaket entwickelt , die seit einiger Zeit für mich funktioniert. Es fügt das obere Verzeichnis dem lib-Pfad hinzu:
Durch das Neuverpacken können mithilfe einer intelligenten Strategie (Überprüfung des Aufrufstapels) relative Importe durchgeführt werden, die in einer Vielzahl von Fällen funktionieren.
quelle
Wenn sich beide Pakete in Ihrem Importpfad (sys.path) befinden und sich das gewünschte Modul / die gewünschte Klasse in example / example.py befindet, versuchen Sie, ohne relativen Import auf die Klasse zuzugreifen:
quelle
Ich denke, die beste Lösung besteht darin, ein Paket für Ihr Modul zu erstellen: Hier finden Sie weitere Informationen dazu.
Sobald Sie ein Paket haben, müssen Sie sich nicht mehr um den relativen Import kümmern, sondern können nur noch absolute Importe durchführen.
quelle
Ich hatte ein ähnliches Problem: Ich brauchte einen Linux-Dienst und ein CGI-Plugin, die gemeinsame Konstanten für die Zusammenarbeit verwenden. Der 'natürliche' Weg, dies zu tun, besteht darin, sie in der init .py des Pakets zu platzieren, aber ich kann das cgi-Plugin nicht mit dem Parameter -m starten.
Meine endgültige Lösung ähnelte der obigen Lösung Nr. 2:
Der Nachteil ist, dass Sie den Konstanten (oder allgemeinen Funktionen) pkg voranstellen müssen:
quelle