HOWTO · Python

Python Importer tous les modules dans un seul répertoire

Ce tutoriel explique comment importer tous les modules dans un répertoire en Python.

Sur cette page

Pour importer des modules depuis un même répertoire, traitez ce répertoire comme un package Python. Si l’ensemble des modules est fixe, indiquez les imports dans le fichier __init__.py du package ; si leurs noms ne sont pas connus à l’avance, découvrez-les et importez-les à l’aide de leur nom qualifié dans le package avec importlib.

Importer tous les modules présents dans un même répertoire en Python

Un package permet d’organiser des modules liés et de leur fournir une interface pratique. Par exemple, le package a peut contenir x.py, y.py et z.py, ainsi que __init__.py. L’initialiseur s’exécute lorsque Python importe le package. Il n’importe pas automatiquement les fichiers voisins : ajoutez-y des imports explicites si vous voulez que ces modules soient accessibles comme attributs du package.

L’exemple autonome suivant crée ce package, importe ses modules dans l’initialiseur, puis accède à chacun d’eux par l’intermédiaire de a :

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)

Dans l’exécution fournie, liée à cette version candidate, les deux exemples se sont terminés avec succès, avec un code de sortie 0 et sans sortie standard.

Les imports relatifs dans __init__.py rendent les modules accessibles sous les noms a.x, a.y et a.z après import a. Cette liste explicite est facile à vérifier et donne au package une interface prévisible. Gardez l’initialiseur ciblé : importer un package exécute son initialiseur ainsi que le code de premier niveau des modules importés. Évitez donc d’y placer des opérations sans rapport ou des effets secondaires coûteux.

Utiliser importlib pour importer des modules de façon avancée

Si une autre partie du programme ajoute des modules au fil du temps, une liste d’imports fixe peut ne pas convenir. La découverte adaptée aux packages peut repérer les modules immédiats et les charger à l’aide de leur nom qualifié. Cet exemple autonome crée un package, découvre ses modules avec pkgutil et stocke chaque module importé dans un dictionnaire :

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)

Cet exemple n’écrit rien sur la sortie standard :

Utilisez modules["alpha"] pour accéder à un objet module importé, puis lire ses attributs ou appeler ses fonctions. La découverte présentée ici porte sur les modules immédiats du package et ignore les sous-packages ; modifiez délibérément cette règle si vous devez aussi charger les packages imbriqués. Elle ne filtre pas les modules selon une convention de nommage propre à l’application : ajoutez donc un prédicat si seuls certains modules sont des plugins. Chaque module découvert s’exécute au moment de son import et peut nécessiter des dépendances ou effectuer sa propre initialisation. Une dépendance manquante ou une erreur dans le code d’un module peut interrompre la boucle de chargement. Le chargement dynamique est utile lorsque l’ensemble des modules est réellement extensible ; pour un ensemble connu, les imports explicites sont plus clairs et plus faciles à maintenir. Comme les imports s’exécutent dans le processus courant, limitez la découverte au contenu de packages de confiance. Si l’initialisation de certains packages est coûteuse, séparez l’enregistrement des plugins du chargement des modules et indiquez clairement les noms sur lesquels les appelants peuvent compter.

Conclusion

Utilisez des imports explicites dans __init__.py lorsque les modules publics du package sont connus. Optez pour la découverte adaptée aux packages avec importlib si le programme doit charger un ensemble de modules qui évolue, et expliquez clairement aux appelants les règles de découverte ainsi que les effets secondaires des imports.