Python-Pakete und -Module

ChangeLog

--2020-04-18: Das Verhalten im Modul __main__ wurde korrigiert.

Überblick

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.

Motivation

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.

Verweise

Ich habe die Version 3.8.2 von beiden durchsucht.

Modul = Datei

Beachten Sie, dass das Lernprogramm die Datei der obersten Ebene liest, dh die Datei, die von Python als ** Skript ** gestartet wurde.

Importeur

Das einfachste

--import Anweisung import m importiert ** Modul ** m

Ich verstehe.

Importierte Seite

Modul-Suchpfad (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:

  1. Verzeichnis mit ** Skript **, aktuelles Verzeichnis, falls nicht angegeben (z. B. wenn eine interaktive Sitzung von python ausgeführt wurde)
  2. Der Wert der Umgebungsvariablen "PYTHONPATH"
  3. Standard für jede Installation (wahrscheinlich das Verzeichnis, in dem die Bibliothek von pip installiert wird)

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.

Lösen des Beispiels in "Motivation" (1)

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-Pattern

Wenn die Verzeichnisstruktur des Moduls kompliziert ist oder Sie versuchen, einen dynamischen Import zu realisieren,

Es wird gesagt, dass ** ein schlechter Schachzug ist ** [Quelle]. Es scheint richtig, "importlib" vollständig zu nutzen.

Paket = Verzeichnis

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).

Importeur

-- 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.

Von hier aus scheinen viele Leute es nur irgendwie zu verstehen. Ich bin auch so

Importierte Seite

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 ... Anweisung

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. ** **.

Relativer Import aus importiertem Modul

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)

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

Lösen des Beispiels in "Motivation" (2)

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.

Ungelöste Probleme

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 / " ausführen, wird die leere Zeichenfolge "" "nach dem Verzeichnis" test "hinzugefügt. Vielleicht bedeutet dies das aktuelle Verzeichnis.

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.

schließlich

Fehler sind willkommen.

Recommended Posts

Python-Pakete und -Module
Lernen Sie Python-Pakete und -Module kennen
Python-Grundkurs (14 Module und Pakete)
Organisieren Sie Python-Module und -Pakete in einem Chaos
Erhalten Sie ein abstraktes Verständnis der Python-Module und -Pakete
Pakete, die MIDI mit Python Midi und Pretty_Midi verarbeiten
Informationen zu Python Primer-Modulen und bedingten Ausdrücken
Virtuelle Python-Umgebung und Pakete unter Ubuntu
[Python] Packen und verteilen Sie Ihre eigenen Module
[Python] Komprimieren und dekomprimieren
Liste der Python-Module
Python- und Numpy-Tipps
[Python] Pip und Wheel
Python Iterator und Generator
Vue-Cli- und Python-Integration
Ruby, Python und Map
Python-Eingabe und Ausgabe
Verwenden von Python # externen Paketen
Python und Ruby teilen sich
Python asyncio und ContextVar
Python - Erläuterung und Zusammenfassung der Verwendung der 24 wichtigsten Pakete
Verwalten Sie Python-Laufzeitpakete und Entwicklungsumgebungspakete mit Poetry
Programmieren mit Python und Tkinter
Ver- und Entschlüsselung mit Python
Python: Klassen- und Instanzvariablen
3-3, Python-Zeichenfolge und Zeichencode
Python 2-Serie und 3-Serie (Anaconda Edition)
Python und Hardware-Verwenden von RS232C mit Python-
Python auf Ruby und wütend Ruby auf Python
Python-Einzug und String-Format
[Python] Laden von selbst erstellten mehrstufigen Modulen
Python Real Number Division (/) und Integer Division (//)
Installieren Sie Python und Flask (Windows 10)
Informationen zu Python-Objekten und -Klassen
Informationen zu Python-Variablen und -Objekten
Apache mod_auth_tkt und Python AuthTkt
Å (Ongustorome) und NFC @ Python
# 2 [python3] Trennung und Kommentar aus
Flache Python-Kopie und tiefe Kopie
Python und Ruby Slice Memo
Python-Installation und grundlegende Grammatik
Ich habe Java und Python verglichen!
Flache Python-Kopie und tiefe Kopie
Über Python, len () und randint ()
Informationen zu Python-Datums- und Zeitzone
Installieren Sie Python 3.7 und Django 3.0 (CentOS)
Python-Umgebungskonstruktion und TensorFlow
Python-Klassen- und Instanzvariablen
Roadmap zum Veröffentlichen von Python-Paketen
Ruby- und Python-Syntax ~ branch ~
[Python] Python und Sicherheit - is Was ist Python?
Stapel und Warteschlange in Python
Python-Metaklasse und SQLalchemie deklarativ
Implementierung von Fibonacci und Primzahlen (Python)
Python-Grundlagen: Bedingungen und Iterationen
Python-Bitoperator und logische Summe
[Hinweis] Klassen, Module, Pakete, Bibliotheken
Python-Debug- und Testmodul