(c) 2023 Technische Hochschule Augsburg - Fakultät für Informatik - Prof.Dr.Nik Klever - Impressum
Pakete sind eine Möglichkeit, den Namensraum von Python Modulen zu strukturieren, indem "mit Punkten getrennte Modulnamen" verwendet werden. Beispielsweise bezeichnet der Modulname A.B ein Submodul mit dem Namen B in einem Paket namens A. Genau wie bei der Nutzung von Modulen den Autoren unterschiedlicher Module erspart wird, sich um die globalen Variablennamen der anderen zu kümmern, erspart die Verwendung von mit Punkten getrennter Modulnamen die Autoren von Multimodul-Paketen wie NumPy oder der Python Imaging Library, sich um die Modulnamen der anderen zu kümmern.
Angenommen, es soll eine Sammlung von Modulen (ein "Paket") für die einheitliche Bearbeitung von Sound-Dateien und Sound-Daten entwickelt werden. Es gibt viele verschiedene Sound-Dateiformate (die in der Regel an ihrer Dateinamenerweiterung, zum Beispiel: .wav, .aiff, .au erkannt werden), so werden möglicherweise eine wachsende Anzahl von Modulen für die Umwandlung zwischen den verschiedenen Dateiformate erstellt und anschliessend auch gepflegt werden müssen. Es gibt auch viele verschiedene Operationen, die auf Sound-Daten ausgeführt werden können (z.B. Mischen, Hinzufügen von Echo, Anwenden einer Equalizer-Funktion, Erstellen eines künstlichen Stereoeffekts), so dass zusätzlich eine ganze Menge an Modulen für solche Operationen erstellt werden muss. Die folgende hierarchische Dateistruktur ist damit eine mögliche Struktur für das entsprechende Paket:
sound/ Oberste Ebene des Pakets (Top-level package)
__init__.py Initialisierung des Sound Pakets
formats/ Unterpaket für die Dateiformat-Konvertierung
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Unterpaket für Sound Efekte
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Unterpaket für Filter
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
Beim Importieren eines Pakets durchsucht Python die Verzeichnisse in der Variablen sys.path nach einem Verzeichnis mit dem Namen des Pakets.
Die Dateien __init__.py sind erforderlich, damit Python die Verzeichnisse als Pakete behandelt. Dies ist notwendig, damit verhindert wird, dass Verzeichnisse mit einem gebräuchlichen Namen, wie z. B. string, andere Module oder Pakete unbeabsichtigt auszublenden, die später auf dem Modul-Suchpfad auftreten. Im einfachsten Fall kann __init__.py nur eine leere Datei sein, aber es kann auch Initialisierungscode für das Paket ausgeführt werden oder die Variable __all__ setzen, die später beschrieben wird.
Module aus einem Paket können auch einzeln importiert werden, zum Beispiel:
import sound.effects.echo
Diese Zeile lädt das Submodul sound.effects.echo. Es muss mit seinem vollständigen Namen referenziert werden:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
Eine alternative Möglichkeit ist die folgende:
from sound.effects import echo
Diese Zeile lädt ebenfalls das Submodul sounds.effects.echo und stellt es ohne sein Paketpräfix sounds.effects zur Verfügung, so dass es wie folgt verwendet werden kann:
echo.echofilter(input, output, delay=0.7, atten=4)
Eine weitere Variation besteht darin, die gewünschte Funktion oder Variable direkt zu importieren:
from sound.effects.echo import echofilter
Auch hier wird das Submodul sounds.effects.echo verwendet, aber nur die entsprechende Funktion echofilter() wird direkt verfügbar:
echofilter(input, output, delay=0.7, atten=4)
Man muss beachten, dass bei der Verwendung von from ... import ... das entsprechende importierte Objekt entweder ein Submodul (oder ein Unterpaket) des Pakets oder ein anderer im Paket definierter Name wie eine Funktion, eine Klasse oder eine Variable sein kann. Die Importanweisung prüft zuerst, ob das Objekt im Paket definiert ist. Wenn nicht, geht es davon aus, dass es ein Modul ist und versucht, es zu laden. Wenn es nicht gefunden wird, wird eine ImportError-Fehlermeldung ausgelöst.
Im Gegensatz dazu muss bei der Verwendung der Syntax import item.subitem.subsubitem jedes item außer dem letzten ein Paket sein; das letzte Objekt kann ein Modul oder ein Paket sein, aber keine Klasse oder Funktion oder Variable wie in der vorherigen Syntax.
%%Mooc MoocStringAssessment
Nun, was passiert, wenn ein Benutzer ** from sound.effects import *** verwendet ? Idealerweise würde man hoffen, dass aus der hirearchischen Struktur der Verzeichnisse des Pakets herausgefunden wird, welche Submodule im Paket vorhanden sind und alle importiert werden. Dies könnte lange dauern und das Importieren von Submodulen kann unerwünschte Nebenwirkungen haben, die nur dann auftreten sollten, wenn das Submodul explizit importiert wird.
Die einzige Lösung ist, dass der Paketautor einen expliziten Index des gesamten Pakets zur Verfügung stellt. Die Import-Anweisung verwendet daher die folgende Konvention: wenn in der Datei __init__.py eines Pakets eine Liste mit dem Namen __all__ definiert ist, wird diese Liste als Liste von Modulnamen interpretiert, die importiert werden sollen, wenn vom Paketimport * angetroffen wird. Es liegt in der Verantwortung des Paketautors, diese Liste auf den neuesten Stand zu halten, wenn eine neue Version des Pakets veröffentlicht wird. Paketautoren können sich aber auch dafür entscheiden eine solche Liste nicht zu definieren, wenn sie import * für ihr Paket nicht für sinnvoll erachten. Zum Beispiel könnte also die Datei sound/effects/__init__.py den folgenden Code enthalten:
__all__ = ["echo", "surround", "reverse"]
Dies würde bedeuten, dass from sound.effects import * die drei benannten Submodule des Soundpakets importieren würde.
Wenn __all__ nicht definiert ist, importiert die Anweisung from sound.effects import * nicht alle Submodule aus dem Paket sound.effects in den aktuellen Namensraum; es stellt nur sicher, dass das Paket sound.effects importiert wurde (einschliesslich der Ausführung von allem Code, der in der Datei __init__.py aufgeführt ist) und importiert dann, welche Namen im Paket definiert sind. Dazu gehören alle in __init__.py definierten Namen (sowie explizit geladenen Submodule). Natürlich sind auch beliebige Untermodule des Pakets im aktuellen Namensraum enthalten, die explizit durch vorherige Importanweisungen geladen wurden. Betrachtet man den folgenden Code:
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
In diesem Beispiel werden die kompletten Untermodule echo und surround in den aktuellen Namensraum mit der from sound.effects import *-Anweisung importiert, da ja die Namen der Module bereits durch die beiden Import-Anweisungen schon in den aktuellen Namensraum eingefügt wurden (Dies funktioniert auch, wenn __all__ definiert ist.)
Obwohl bestimmte Module nur entwickelt worden sind, um Namen mit bestimmten Mustern zu exportieren, wenn import * verwendet wird, wird import * immer noch als schlechte Praxis in einem produktiven Code angesehen.
Man bedenke, dass die Verwendung from paket import spezifisches_submodul nicht falsch ist. Im Gegenteil, diese Notation wegen der verständlicheren Lesbarkeit eher empfohlen auch wenn sich dadurch der Code evtl. vergrößert.
%%Mooc MoocMultipleChoiceAssessment
Aus dem Paket math werden die trigonometrischen Funktionen sin, cos und tan benötigt:
Wenn Pakete in Unterpakete strukturiert sind (wie in dem obigen Paket sound als Beispiel), können absolute Importe verwendet werden, um auf Submodule von Geschwisterpaketen zu verweisen. Zum Beispiel, wenn das Modul sound.filters.vocoder das Modul echo aus dem Paket sound.effects verwenden muss, kann from sound.effects import echo benutzt werden.
Allerdings können mit der Syntax from ... import ... auch relative Importe verwendet werden. Für diese Fälle werden führende Punkte analog wie in der Syntax von Dateisystemen verwendet, um die aktuellen und übergeordneten Pakete anzuzeigen, die an dem relativen Import beteiligt sind. Aus dem Modul surround heraus können die folgenden relativen Importe verwendet werden:
from . import echo
from .. import formats
from ..filters import equalizer
Man beachte, dass relative Importe auf den Namen des aktuellen Moduls basieren. Da der Name des Hauptmoduls immer "main" ist, müssen Module, die für die Verwendung als Hauptmodul einer Python-Anwendung gedacht sind, immer absolute Importe verwenden.
Pakete unterstützen ein weiteres spezielles Attribut, __path__. Dieses Attribut ist eine Liste und wird mit dem Namen des Verzeichnisses initialisiert, die die Datei __init__.py enthält bevor der Code in dieser Datei ausgeführt wird. Dieses Attribut kann geändert werden und beeinträchtigt damit zukünftige Suchanfragen für Module und Unterpakete, die im Paket enthalten sind.
Obwohl dieses Attribut nicht oft benötigt wird, kann es dazu verwendet werden, die Menge von Modulen zu erweitern, die in einem Paket gefunden werden.
%%Mooc Video
%%Mooc WebReference