2.19. Module schreiben

Jede .py-Datei ist ein Modul. Das Aufteilen eines wachsenden Skripts auf mehrere Dateien hält jede Datei kurz und ermöglicht es, gemeinsame Hilfsfunktionen zwischen Skripten zu teilen.

2.19.1. Ein Skript aufteilen

Ziehen Sie eine zusammengehörige Gruppe von Funktionen in eine eigene Datei heraus:

camera_utils.py:

def banner():
    print("OpenMV")

def label(text):
    return "[" + text + "]"

main.py:

import camera_utils

camera_utils.banner()
print(camera_utils.label("ready"))

Ausgabe:

OpenMV
[ready]

Die beiden Dateien liegen nebeneinander im selben Verzeichnis. Wenn main.py ausgeführt wird, liest import camera_utils die Datei camera_utils.py einmal, führt ihre Anweisungen der obersten Ebene aus und bindet das resultierende Modulobjekt an den Namen camera_utils in main. Ein zweites import camera_utils von irgendwoher gibt dasselbe Objekt zurück – Module werden nach ihrem ersten Laden zwischengespeichert.

Der Name eines Moduls stammt von seinem Dateinamen, sodass camera_utils.py als import camera_utils importiert wird.

2.19.2. Mehrdatei-Module (Pakete)

Ein Modul kann auch ein Verzeichnis von Dateien sein statt einer einzelnen .py. Der Name des Verzeichnisses wird zum Modulnamen, und die darin enthaltenen Dateien sind seine Submodule:

camera_utils/
    __init__.py
    text.py
    timing.py

__init__.py ist die Datei, die ausgeführt wird, wenn das Paket selbst importiert wird; sie kann leer sein oder ausgewählte Namen aus den Submodulen erneut exportieren. Die Submodule werden mit einem Punktnamen erreicht:

import camera_utils.text
from camera_utils.timing import elapsed

camera_utils.text.label("ready")

Innerhalb des Pakets können Submodule einander entweder über den vollständigen Punktnamen oder über einen relativen Import erreichen, der einen führenden Punkt verwendet, um „dieses Paket“ zu bedeuten:

camera_utils/timing.py:

from . import text             # the sibling submodule

def stamp(value):
    return text.label(str(value))

Um stattdessen einen bestimmten Namen zu holen, benennen Sie ihn nach dem mit Punkt versehenen Geschwister:

from .text import label

def stamp(value):
    return label(str(value))

Relative Importe halten ein Paket in sich geschlossen: Das Umbenennen des Paketverzeichnisses erfordert nicht, jedes Submodul zu bearbeiten.

Verwenden Sie ein Paket, wenn eine einzelne Datei über eine angenehme Größe hinauswächst oder wenn eine Gruppe verwandter Module unter einem Namensraum zusammengehört. Für alltägliche Skripte genügt eine einzelne .py-Datei.

2.19.3. Der __name__-Schutz

Jedes Modul hat einen eingebauten Namen __name__. Sein Wert hängt davon ab, wie die Datei verwendet wird:

  • Wenn die Datei direkt ausgeführt wird, wird __name__ auf die Zeichenkette "__main__" gesetzt.

  • Wenn die Datei von einem anderen Skript importiert wird, wird __name__ auf den Modulnamen gesetzt – den Dateinamen ohne .py.

Die Redewendung, die dies nutzt, lautet:

label_util.py:

def label(text):
    return "[" + text + "]"

if __name__ == "__main__":
    print(label("self-test"))

Ausgabe (bei direkter Ausführung):

[self-test]

Wenn dieselbe Datei stattdessen importiert wird, wird __name__ auf den Modulnamen gesetzt, sodass der if-Block übersprungen wird und nichts Zusätzliches ausgeführt wird:

>>> import label_util
>>> label_util.__name__
'label_util'

Verwenden Sie das Muster, um einen schnellen Rauchtest oder eine Demo an eine Bibliotheksdatei anzuhängen, ohne Skripte zu stören, die sie importieren.