2009-06-04 36 views
40

En python, ¿cómo se agregan módulos dinámicamente a un paquete mientras se está ejecutando su programación?Carga dinámica de módulos python

Quiero ser capaz de añadir módulos al directorio del paquete de un proceso exterior, y ser capaz de utilizar los nuevos módulos en mi programa:

import package 

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

¿Cómo se hace esto?

+0

posible duplicado de [la carga dinámica de módulos de Python] (http://stackoverflow.com/questions/769534/dynamic-loading-of- python-modules) –

+0

Posible duplicado de [Importación de módulos dinámicos en Python] (https://stackoverflow.com/questions/301134/dynamic-module-import-in-python) – scai

Respuesta

0

Agregue el directorio del módulo al sys.path y use la instrucción normal import.

+3

Esto no resuelve el problema. El nombre del módulo es especificado por el usuario y la importación normal no parece tomar una cadena. Además, no detectará los módulos "nuevos". –

47

Su código es casi correcto.

Véase la función __import__.

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

__import__ devuelve el módulo, por lo que puede hacer "mod = __import __ (name)" en lugar de tener que buscarlo en sys.modules. – Brian

+0

"Cuando la variable de nombre es de la forma package.module, normalmente, se devuelve el paquete de nivel superior (el nombre hasta el primer punto), no el módulo nombrado por nombre." –

+1

Aclaración al comentario de Brian ... __import__ devuelve el paquete, no el módulo. Por lo tanto, necesita buscar el módulo en sys.modules o usar mi técnica (enumerada en otra respuesta) para obtener el módulo del paquete. –

3

para detectar cambios en un directorio, en Linux, puede utilizar pyinotify (here es un buen ejemplo de trabajo); en una Mac, fsevents (a través del paquete PyObjC que viene con su Mac); en Windows, Directory Change Notifications a través de win32api (o el módulo de la biblioteca estándar de Python ctypes). AFAIK, nadie ha completado estos diversos enfoques en un paquete portátil. (Por supuesto, en el peor de los casos, puede recurrir a enfoques de "tecnología más baja", como el sondeo periódico, como Tim Golden's article, quizás con un toque de "alertas de un proceso externo" a través de una señal, etc.).

Una vez que tenga la notificación y el nombre del módulo nuevo o modificado, debería funcionar el código que muestra en la pregunta.

18

Bastien ya ha respondido a la pregunta, de todos modos se puede encontrar útil esta función que utilizo para cargar todos los módulos de una subcarpeta en un diccionario:

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 

Esta otra es crear una instancia de un objeto de una clase definida en uno de los módulos cargados por la primera función:

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) 

una forma sencilla de utilizar esas funciones es la siguiente:

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

Obviamente, puede reemplazar todas las referencias de subcarpetas de "servicios" por parámetros.

+0

sobre la pregunta "__import__ cargar el paquete, no el módulo" (no puedo comentar la respuesta de Bastian): en realidad __import__ cargar el módulo en lugar del paquete cuando se utiliza el parámetro fromlist, incluso como [] (verifique mi función) –

+0

loadModules() devuelve también * .pyc ¿es importante? – DrFalk3n

+0

uhm help for os.path.exists() no dice nada acerca de una coincidencia parcial, creo que _no_ debe devolver archivos * .pyc. De todos modos, en este caso, creo que no es realmente importante. –

9

Un truco con la respuesta de Bastien ... La función __import__() devuelve el objeto del paquete, no el objeto del módulo. Si usa la siguiente función, cargará dinámicamente el módulo del paquete y le devolverá el módulo, no el paquete.

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

entonces usted puede hacer:

mod = my_import('package.' + name) 
mod.doSomething() 
+0

Tenga en cuenta que no es "mi" respuesta, simplemente agregué un poco de formato a la respuesta de S. Lott. –

3
import importlib 

module = importlib.import_module('my_package.my_module') 
my_class = getattr(module, 'MyClass') 
my_instance = my_class() 
Cuestiones relacionadas