HOWTO · Python

Python Import aller Module in einem Verzeichnis

In diesem Tutorial wird erläutert, wie Sie alle Module in ein Verzeichnis in Python importieren.

Auf dieser Seite

Behandle das Verzeichnis als Python-Paket, um Module daraus zu importieren. Bei einer festen Modulmenge listest du die Importe in der __init__.py des Pakets auf. Sind die Modulnamen nicht im Voraus bekannt, kannst du sie anhand ihrer qualifizierten Paketnamen mit importlib ermitteln und importieren.

Alle Module aus einem Verzeichnis in Python importieren

Ein Paket kann zusammengehörige Module organisieren und eine praktische Schnittstelle zu ihnen bereitstellen. Das Paket a kann beispielsweise neben der __init__.py die Dateien x.py, y.py und z.py enthalten. Die Initialisierungsdatei wird ausgeführt, wenn Python das Paket importiert. Die benachbarten Dateien werden dadurch nicht automatisch importiert. Füge dort explizite Importe ein, wenn diese Module als Attribute des Pakets verfügbar sein sollen.

Das folgende eigenständige Beispiel erstellt dieses Paket, importiert seine Module in der Initialisierungsdatei und greift über a auf die einzelnen Module zu:

from pathlib import Path
import sys
from tempfile import TemporaryDirectory

with TemporaryDirectory() as temp_dir:
    package_dir = Path(temp_dir) / "a"
    package_dir.mkdir()
    (package_dir / "x.py").write_text("value = 'X'\n", encoding="utf-8")
    (package_dir / "y.py").write_text("value = 'Y'\n", encoding="utf-8")
    (package_dir / "z.py").write_text("value = 'Z'\n", encoding="utf-8")
    (package_dir / "__init__.py").write_text(
        "from . import x, y, z\n", encoding="utf-8"
    )
    sys.path.insert(0, temp_dir)
    try:
        import a
        assert (a.x.value, a.y.value, a.z.value) == ("X", "Y", "Z")
    finally:
        sys.path.remove(temp_dir)
        for module_name in list(sys.modules):
            if module_name == "a" or module_name.startswith("a."):
                sys.modules.pop(module_name, None)

Bei der bereitgestellten, an den Kandidaten gebundenen Ausführung wurden beide Beispiele erfolgreich abgeschlossen: mit Exitcode 0 und ohne Standardausgabe.

Dank der relativen Importe in der __init__.py sind die Module nach import a unter a.x, a.y und a.z verfügbar. Diese explizite Liste lässt sich leicht überprüfen und sorgt für eine vorhersehbare Paketschnittstelle. Halte die Initialisierungsdatei schlank: Beim Import eines Pakets werden sowohl diese Datei als auch der Top-Level-Code der importierten Module ausgeführt. Vermeide daher dort sachfremde Aufgaben oder kostspielige Nebenwirkungen.

importlib für den erweiterten Modulimport verwenden

Wenn ein anderer Teil des Programms im Laufe der Zeit Module hinzufügt, ist eine feste Importliste möglicherweise ungeeignet. Die paketbezogene Suche kann direkte Module finden und anhand ihres qualifizierten Namens laden. Dieses eigenständige Beispiel erstellt ein Paket, sucht mit pkgutil darin nach Modulen und speichert jedes importierte Modul in einem Wörterbuch:

from pathlib import Path
import importlib
import pkgutil
import sys
from tempfile import TemporaryDirectory

with TemporaryDirectory() as temp_dir:
    package_dir = Path(temp_dir) / "plugins"
    package_dir.mkdir()
    (package_dir / "__init__.py").write_text("", encoding="utf-8")
    (package_dir / "alpha.py").write_text("value = 1\n", encoding="utf-8")
    (package_dir / "beta.py").write_text("value = 2\n", encoding="utf-8")
    sys.path.insert(0, temp_dir)
    try:
        package = importlib.import_module("plugins")
        modules = {
            item.name: importlib.import_module(f"{package.__name__}.{item.name}")
            for item in pkgutil.iter_modules(package.__path__)
            if not item.ispkg
        }
        assert sorted(modules) == ["alpha", "beta"]
        assert modules["alpha"].value == 1
        assert modules["beta"].value == 2
    finally:
        sys.path.remove(temp_dir)
        for module_name in list(sys.modules):
            if module_name == "plugins" or module_name.startswith("plugins."):
                sys.modules.pop(module_name, None)

Dieses Beispiel gibt nichts auf der Standardausgabe aus:

Mit modules["alpha"] erhältst du Zugriff auf ein importiertes Modulobjekt und kannst anschließend dessen Attribute lesen oder seine Funktionen aufrufen. Die Suche im Beispiel berücksichtigt direkte Module des Pakets, überspringt aber Unterpakete. Passe dieses Verhalten bewusst an, wenn auch verschachtelte Pakete geladen werden sollen. Das Beispiel filtert nicht anhand einer anwendungsspezifischen Namenskonvention. Wenn nur bestimmte Module als Plugins infrage kommen, ergänze daher eine passende Bedingung. Jedes gefundene Modul wird beim Import ausgeführt und kann Abhängigkeiten benötigen oder eigene Initialisierungsschritte vornehmen. Eine fehlende Abhängigkeit oder ein Fehler im Modulcode kann die Ladeschleife unterbrechen. Dynamisches Laden ist sinnvoll, wenn die Modulmenge tatsächlich erweiterbar ist. Bei einer bekannten Modulmenge sind explizite Importe jedoch klarer und leichter zu warten. Da der Import im aktuellen Prozess stattfindet, solltest du nur vertrauenswürdige Paketdateien durchsuchen. Dauert die Initialisierung mancher Module lange, trenne die Plugin-Registrierung vom Laden der Module und dokumentiere, auf welche Namen sich Aufrufer verlassen können.

Fazit

Verwende explizite Importe in der __init__.py, wenn die öffentlichen Module eines Pakets bekannt sind. Muss das Programm dagegen eine veränderliche Modulmenge laden, nutze die paketbezogene Suche mit importlib und erläutere den Aufrufern die Suchregeln sowie mögliche Nebenwirkungen der Importe.