python模块的动态加载

发布于 2024-07-23 18:35:15 字数 261 浏览 9 评论 0原文

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

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

import package

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

我该如何做到这一点?

In python how do you dynamically add modules to a package while your program is running.

I want to be able to add modules to the package directory from an outside process, and be able to use those new modules in my program:

import package

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

How do I do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

月亮坠入山谷 2024-07-30 18:35:15

你的代码几乎是正确的。

请参阅 __import__ 函数。

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

Your code is almost correct.

See __import__ function.

def doSomething(name):
    name = "package." + name
    mod = __import__(name, fromlist=[''])
    mod.doSomething()
夜夜流光相皎洁 2024-07-30 18:35:15

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

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

Bastien already answered the question, anyway you may find useful this function I use to load all the modules from a subfolder in a dictionary:

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

This other one is to instantiate an object by a class defined in one of the modules loaded by the first function:

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)

A simple way to use those functions is this:

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

Obviously you can replace all the "services" subfolder references with parameters.

瑾夏年华 2024-07-30 18:35:15
import importlib

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

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
冰魂雪魄 2024-07-30 18:35:15

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

One trick with Bastien's answer... The __import__() function returns the package object, not the module object. If you use the following function, it will dynamically load the module from the package and return you the module, not the package.

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

Then you can do:

mod = my_import('package.' + name)
mod.doSomething()
无所的.畏惧 2024-07-30 18:35:15

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

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

To detect changes to a directory, on Linux, you can use pyinotify (here is a nice working example); on a Mac, fsevents (via the PyObjC package that comes with your Mac); on Windows, Directory Change Notifications via win32api (or the Python standard library ctypes module). AFAIK, nobody's wrapped up these various approaches into one portable package. (Of course, worst case, you can fall back to "lower tech" approaches such as periodic polling, as Tim Golden's article, perhaps with a touch of "alerts from an external process" via a signal, etc).

Once you do have the notification and the name of the new or modified module, the code you show in the question should work.

冧九 2024-07-30 18:35:15

将模块目录添加到 sys.path 并使用普通的 import 语句。

Add the module directory to sys.path and use the normal import statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文