web-dev-qa-db-fra.com

Chargement dynamique des modules python

Dans python comment ajouter dynamiquement des modules à un paquet pendant que votre programme est en cours d'exécution.

Je veux pouvoir ajouter des modules au répertoire du package à partir d'un processus externe et pouvoir utiliser ces nouveaux modules dans mon programme:

import package

def doSomething(name):
    pkg = __import__("package." + name)
    mod = getattr(pkg, name)
    mod.doSomething()

Comment puis-je faire cela?

50
No Name

Votre code est presque correct.

Voir __import__ fonction.

def doSomething(name):
    name = "package." + name
    mod = __import__(name, fromlist=[''])
    mod.doSomething()
55
S.Lott

Bastien a déjà répondu à la question, de toute façon vous pouvez trouver utile cette fonction que j'utilise pour charger tous les modules d'un sous-dossier dans un dictionnaire:

def loadModules():
    res = {}
    import os
    # check subfolders
    lst = os.listdir("services")
    dir = []
    for d in lst:
        s = os.path.abspath("services") + os.sep + d
        if os.path.isdir(s) and os.path.exists(s + os.sep + "__init__.py"):
            dir.append(d)
    # load the modules
    for d in dir:
        res[d] = __import__("services." + d, fromlist = ["*"])
    return res

Cette autre consiste à instancier un objet par une classe définie dans l'un des modules chargés par la première fonction:

def getClassByName(module, className):
    if not module:
        if className.startswith("services."):
            className = className.split("services.")[1]
        l = className.split(".")
        m = __services__[l[0]]
        return getClassByName(m, ".".join(l[1:]))
    Elif "." in className:
        l = className.split(".")
        m = getattr(module, l[0])
        return getClassByName(m, ".".join(l[1:]))
    else:
        return getattr(module, className)

Une façon simple d'utiliser ces fonctions est la suivante:

mods = loadModules()
cls = getClassByName(mods["MyModule"], "submodule.filepy.Class")
obj = cls()

Évidemment, vous pouvez remplacer toutes les références de sous-dossier "services" par des paramètres.

23
Giorgio Gelardi
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
12
Eds_k

Une astuce avec la réponse de Bastien ... La fonction __import__() renvoie l'objet package, pas l'objet module. Si vous utilisez la fonction suivante, elle chargera dynamiquement le module à partir du package et vous renverra le module, pas le package.

def my_import(name):
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

Ensuite, vous pouvez faire:

mod = my_import('package.' + name)
mod.doSomething()
9
Clint Miller

Pour détecter les changements dans un répertoire, sous Linux, vous pouvez utiliser pyinotify ( ici est un bel exemple de travail); sur un Mac, fsevents (via le package PyObjC fourni avec votre Mac); sous Windows, Notifications de changement de répertoire via win32api (ou le module Python bibliothèque standard ctypes). AFAIK, personne n'est encapsulé ces différentes approches dans un seul package portable. (Bien sûr, dans le pire des cas, vous pouvez revenir à des approches "low tech" telles que les sondages périodiques, comme article de Tim Golden , peut-être avec une touche "d'alertes") d'un processus externe "via un signal, etc.).

Une fois que vous avez la notification et le nom du module nouveau ou modifié, le code que vous montrez dans la question devrait fonctionner.

3
Alex Martelli