60

在 python 中,如何在程序运行时动态地将模块添加到包中。

我希望能够从外部进程将模块添加到包目录中,并能够在我的程序中使用这些新模块:

import package

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

我该怎么做呢?

4

6 回答 6

60

您的代码几乎是正确的。

__import__功能。

def doSomething(name):
    name = "package." + name
    mod = __import__(name, fromlist=[''])
    mod.doSomething()
于 2009-06-04T15:25:02.743 回答
23

Bastien 已经回答了这个问题,无论如何你可能会发现我用来从字典中的子文件夹加载所有模块的这个函数很有用:

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()

显然,您可以用参数替换所有“服务”子文件夹引用。

于 2009-06-04T16:33:38.880 回答
16
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
于 2017-09-29T06:38:17.487 回答
9

Bastien 回答的一个技巧......该__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()
于 2009-06-04T17:02:57.113 回答
3

要检测目录的更改,在 Linux 上,您可以使用pyinotify是一个很好的工作示例);在 Mac 上,fsevents(通过 Mac 附带的 PyObjC 包);在 Windows 上,通过win32api(或 Python 标准库模块)的目录更改通知。AFAIK,没有人将这些不同的方法打包成一个便携包。(当然,在最坏的情况下,您可以回退到“低技术”方法,例如定期轮询,如Tim Golden 的文章,也许通过信号等“来自外部进程的警报”)。ctypes

一旦您获得通知和新模块或修改模块的名称,您在问题中显示的代码应该可以工作。

于 2009-06-04T15:26:31.943 回答
-1

将模块目录添加到sys.path并使用正常import语句。

于 2009-06-04T15:02:24.537 回答