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?
Votre code est presque correct.
Voir __import__
fonction.
def doSomething(name):
name = "package." + name
mod = __import__(name, fromlist=[''])
mod.doSomething()
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.
import importlib
module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
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()
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.