Динамическая загрузка модулей Python - PullRequest
49 голосов
/ 04 июня 2009

Как в Python динамически добавлять модули в пакет во время работы вашего программирования.

Я хочу иметь возможность добавлять модули в каталог пакетов из внешнего процесса и использовать эти новые модули в моей программе:

import package

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

Как мне это сделать?

Ответы [ 6 ]

55 голосов
/ 04 июня 2009

Ваш код почти правильный.

См. __import__ функция.

def doSomething(name):
    name = "package." + name
    mod = __import__(name, fromlist=[''])
    mod.doSomething()
22 голосов
/ 04 июня 2009

Бастьен уже ответил на вопрос, в любом случае вам может пригодиться эта функция, которую я использую для загрузки всех модулей из подпапки в словаре:

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

Это еще один экземпляр объекта с помощью класса, определенного в одном из модулей, загруженных первой функцией:

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)

Простой способ использования этих функций заключается в следующем:

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

Очевидно, что вы можете заменить все ссылки на подпапки "services" параметрами.

9 голосов
/ 29 сентября 2017
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
9 голосов
/ 04 июня 2009

Один трюк с ответом Бастьена ... Функция __import__() возвращает объект пакета, а не объект модуля. Если вы используете следующую функцию, она динамически загрузит модуль из пакета и вернет вам модуль, а не пакет.

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

Тогда вы можете сделать:

mod = my_import('package.' + name)
mod.doSomething()
3 голосов
/ 04 июня 2009

Чтобы обнаружить изменения в каталоге, в Linux вы можете использовать pyinotify ( здесь - хороший рабочий пример); на Mac fsevents (через пакет PyObjC, поставляемый с Mac); в Windows Уведомления об изменении каталога через win32api (или модуль стандартной библиотеки Python ctypes). AFAIK, никто не обернул эти разные подходы в одну портативную упаковку. (Конечно, в худшем случае вы можете прибегнуть к «низкотехнологичным» подходам, таким как периодический опрос, например статья Тима Голдена , возможно, с оттенком «оповещения от внешнего процесса» через сигнал и т. Д. ).

Как только у вас появятся уведомление и название нового или измененного модуля, код, указанный в вопросе, должен работать.

0 голосов
/ 04 июня 2009

Добавьте каталог модуля в sys.path и используйте обычный оператор import.

...