ChangeLog
--2020-04-18: Das Verhalten im Modul __main__
wurde korrigiert.
Werfen wir einen kurzen Blick auf Python-Pakete und -Module, die viele Leute zu verstehen und zu verwenden scheinen. Es ist nicht sehr gut organisiert, also werde ich es eines Tages umschreiben.
Ich habe Python-Code für maschinelles Lernen, der von jemand anderem geschrieben wurde.
Es gibt eine Datei, die Sie am Speicherort ". / A / b / c.py" aus dem aktuellen Verzeichnis ausführen möchten.
Diese Datei ist in einem gemeinsamen Format geschrieben, das je nach Wert von __name__
auf zwei Arten funktioniert.
Im Inneren wird . / A / b / d.py
von import d
** absolut importiert **.
Unter der Annahme, dass "PYTHONPATH" als Standard beibehalten wird, funktioniert dies korrekt, wenn es auf "Python. / A / b / c.py" eingestellt ist oder wenn "abc" aus einer anderen Python-Datei importiert wird. Wird sich verändern. Wenn das letztere nicht funktioniert, wenn das erstere funktioniert, und das letztere funktioniert, wenn Sie "c.py" zum relativen Import umschreiben, funktioniert das erstere nicht. Was zum Teufel ist los, ist die Motivation, diesmal zu lernen.
Lassen Sie mich die letztere Situation etwas näher erläutern. Sie führen pytest ./test/<test file> .py
oder python -m pytest ./test/ <testdatei> .py
zum Testen von Einheiten aus. Der Punkt ist, dass sich die Testdatei nicht im aktuellen Verzeichnis befindet.
Ich habe die Version 3.8.2 von beiden durchsucht.
Beachten Sie, dass das Lernprogramm die Datei der obersten Ebene liest, dh die Datei, die von Python
Das einfachste
--import Anweisung import m
importiert ** Modul ** m
m
ist eine ** Python-Datei ** namens m.py
.
--Dies ermöglicht Ihnen den Zugriff auf die ** Namen ** (Variablennamen, Funktionsnamen, Klassennamen usw.), die in m.py
mit m.n
definiert sind.Ich verstehe.
__name__
auf Ihren eigenen Modulnamen verweisen. In dem durch die obige Methode importierten "m" wird "name" zu "m"sys.path
)Im obigen Beispiel befindet sich das zu importierende Modul "m", dh die Datei "m.py", am Anfang der Liste der Verzeichnisse, die auf "sys.path" gesetzt sind (der Wert der Variablen "path" des Moduls "sys"). Es wird in der Reihenfolge von gesucht. Standardmäßig sind sie in der folgenden Reihenfolge angeordnet:
python
ausgeführt wurde)Hier ist der erste der Songwriter. ** Das Verzeichnis mit der ausgeführten Datei wird anstelle des aktuellen Verzeichnisses durchsucht. ** **. Beide sind nur dann gleich, wenn Sie die Datei im aktuellen Verzeichnis ausführen.
Jetzt ist der Grund für das Problem, das ich in "Motivation" geschrieben habe, fast klar. Wenn Sie "Python. / A / b / c.py" mit der vorherigen Methode ausführen, setzt die erste Regel ". / A / b" an den Anfang von "sys.path". Dann wird zuerst "import d" nach diesem Verzeichnis durchsucht, und der Import ist erfolgreich, weil es tatsächlich ". / A / b / d.py" hat.
Wenn andererseits pytest ausgeführt wird, beginnt "sys.path" mit ". / Test", so dass "import abc" nicht an erster Stelle besteht, und selbst wenn dies durch Setzen von "PYTHONPATH" usw. vermieden wird, "c Import d
in .py` vergeht dieses Mal nicht.
Wenn Sie "import d" auf "from .import d" setzen (oder?) Und ** relativer Import **, wird das vorherige Muster sicherlich nicht bestanden [Bestätigung erforderlich].
sys.path
Anti-PatternWenn die Verzeichnisstruktur des Moduls kompliziert ist oder Sie versuchen, einen dynamischen Import zu realisieren,
sys.path
in Ihrem Python-Programm dynamisch umschreibenEs wird gesagt, dass ** ein schlechter Schachzug ist ** [Quelle]. Es scheint richtig, "importlib" vollständig zu nutzen.
Ein ** Paket ** ist eine Sammlung der oben genannten Module in einem Verzeichnis. Der Verzeichnisname wird zum Paketnamen. Verzeichnisse können eine hierarchische Struktur haben, die dem Paketpfad entspricht (punktgetrennte Folge von Paketnamen).
-- import a.b.c
importiert das Modul c
in das Paket a.b
. Importieren Sie also "a / b / c.py" in das Suchzielverzeichnis des Moduls.
p
muss die Datei ** p / __ init __. Py
** enthalten, um als Paket p
erkannt zu werden.Von hier aus scheinen viele Leute es nur irgendwie zu verstehen. Ich bin auch so
Wenn das Paket "p" importiert wird, kann sich die Variable "path" auf die Zeichenfolge beziehen, die das Verzeichnis von "p" in "p / __ init __. Py" darstellt.
from ... import ...
Anweisungc.n
darauf verweisen, indem Sie from a.b import c
einstellen.n
. Wenn Sie from a.b.c import n
ausführen, können Sie danach nur mit n
darauf verweisen.c
. Wie ich vorher erklärt habe
--Paket b
. Mit from a import b
. Danach kann das Paket "b" nur noch mit "b" anstelle von "a.b" referenziert werden.Wenn Sie das Paket "b" wie oben importieren, wird "b / __ init __. Py" ausgeführt. Diese Datei ist standardmäßig leer, und wenn sie leer ist, führt der Interpreter nichts aus. Wenn Sie also ** b
importieren, können Sie nicht auf a.b.c
zugreifen. ** **.
Beim Importieren anderer Module wird normalerweise der oben genannte absolute Import verwendet. Die Suche nach Modulen erfolgt nach sys.path
.
Sie können die obige Anweisung "from import" verwenden, um einen ** relativen Import ** durchzuführen.
Innerhalb des importierten Moduls (dh der .py
-Datei)
from . import m
from .. import m
from ..p import q
Sie können Pakete, Module, Namen usw. relativ zueinander importieren. Zu diesem Zeitpunkt werden .
und ..
basierend auf dem ** Paketpfad ** aufgelöst, in dem das aktuelle Modul vorhanden ist.
Das heißt, wenn das aktuelle Modul "a.b.m" ist, dh das Paket, zu dem es gehört, ist "a.b", dann ist "." "A.b" und ".." ist "a".
Beachten Sie, dass das Modul __main__
keinen Paketpfad hat. Eine wichtige Folge davon ist, dass relative Importe von Modulen, die als ** __main__
ausgeführt werden, nicht möglich sind. ** **.
Außerdem halte ich es für in Ordnung, so etwas wie "import ..p.q.m" zu tun, aber es scheint, dass dies nicht möglich ist
Dies löst die zweite Frage. Wenn Sie a / b / c.py
in einen relativen Import wie aus .import d
umschreiben, funktioniert es einwandfrei, wenn c
mit import abc
importiert wird, aber python a / Bei Ausführung als b / c.py
verhält sich c.py
wie Modul __main__
und der relative Import ist nicht verfügbar. Infolgedessen war ich von dem Verhalten enttäuscht.
Infolgedessen müssen Module, die aus Code importiert wurden, den Sie sowohl als Skripte als auch als Module verwenden möchten, wie z. B. "a / b / c.py" im Beispiel, "pip install" sein. Auf diese Weise können Sie immer den absoluten Import verwenden, sodass Sie beide Methoden verwenden können.
Es wurde auch bestätigt, dass das Importverhalten je nach Start des Pytests unterschiedlich ist.
Ich habe versucht, eine print-Anweisung in das Testskript einzufügen (-s
ist eine Option, um zu verhindern, dass pytest die Standardausgabe erfasst).
--pytest -s test / <test file.py>
startet sys.path
mit dem Verzeichnis test
, gefolgt von den Systemstandards
--Aber wenn Sie "python -m pytest -s test /
Einige der Gründe dafür sind ebenfalls bekannt. Wenn Sie beim Starten des Python-Interpreters ein Modul mit -m
angeben, wird das aktuelle Verzeichnis am Anfang von sys.path
hinzugefügt. In diesem Fall kann es der Pytest sein, nicht der Interpreter, der sys.path`` test
hinzufügt. Denn in diesem Fall ist -s test / <test file.py>
nur ein Argument, nicht der Name des auszuführenden Skripts [Prüfung].
Wenn Sie den Befehl mit pip installieren, überprüfen Sie außerdem die Einstellungen wie "sys.path", wenn Sie den Befehl starten.
Fehler sind willkommen.
Recommended Posts