如何动态加载Python类

pje*_*esi 147 python reflection python-import

给定一个Python类的字符串,例如my_package.my_module.MyClass,加载它的最佳方法是什么?

换句话说,我正在寻找Class.forName()Java中的等效函数,Python中的函数.它需要在Google App Engine上运行.

最好这是一个函数,它接受类的FQN作为字符串,并返回对类的引用:

my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()
Run Code Online (Sandbox Code Playgroud)

Jas*_*ker 181

从python文档中,这是你想要的功能:

def my_import(name):
    components = name.split('.')
    mod = __import__(components[0])
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod
Run Code Online (Sandbox Code Playgroud)

简单__import__不起作用的原因是因为任何超出包字符串中第一个点的内容都是您要导入的模块的属性.因此,这样的事情是行不通的:

__import__('foo.bar.baz.qux')
Run Code Online (Sandbox Code Playgroud)

你必须像这样调用上面的函数:

my_import('foo.bar.baz.qux')
Run Code Online (Sandbox Code Playgroud)

或者就你的例子而言:

klass = my_import('my_package.my_module.my_class')
some_object = klass()
Run Code Online (Sandbox Code Playgroud)

编辑:我有点偏僻.你基本上想要做的是:

from my_package.my_module import my_class
Run Code Online (Sandbox Code Playgroud)

仅当您有一个空的 fromlist时,才需要上述功能.因此,适当的调用将是这样的:

mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')
Run Code Online (Sandbox Code Playgroud)

  • __import__ 的 python 文档(在代码上)说要使用 importlib。所以应该结帐亚当斯宾塞的答案 (2认同)

cha*_*rik 109

如果你不想自己动手,那么pydoc模块中有一个功能就是这样:

from pydoc import locate
my_class = locate('my_package.my_module.MyClass')
Run Code Online (Sandbox Code Playgroud)

这种方法优于此处列出的其他方法的优点是可以在提供的虚线路径上locate找到任何 python对象,而不仅仅是模块中直接的对象.例如my_package.my_module.MyClass.attr.

如果你很好奇他们的食谱是什么,这里的功能是:

def locate(path, forceload=0):
    """Locate an object by name or dotted path, importing as necessary."""
    parts = [part for part in split(path, '.') if part]
    module, n = None, 0
    while n < len(parts):
        nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
        if nextmodule: module, n = nextmodule, n + 1
        else: break
    if module:
        object = module
    else:
        object = __builtin__
    for part in parts[n:]:
        try:
            object = getattr(object, part)
        except AttributeError:
            return None
    return object
Run Code Online (Sandbox Code Playgroud)

它依赖于pydoc.safeimport功能.以下是有关的文档:

"""Import a module; handle errors; return None if the module isn't found.

If the module *is* found but an exception occurs, it's wrapped in an
ErrorDuringImport exception and reraised.  Unlike __import__, if a
package path is specified, the module at the end of the path is returned,
not the package at the beginning.  If the optional 'forceload' argument
is 1, we reload the module from disk (unless it's a dynamic extension)."""
Run Code Online (Sandbox Code Playgroud)

  • 有点偏离主题,但看起来这应该位于与“pydoc”不同的包中。 (3认同)
  • 我赞成这个答案。顺便说一句,这里是也有 safeimport 的代码,因为为此导入 pydoc 似乎很奇怪:https://github.com/python/cpython/blob/aa8ea3a6be22c92e774df90c6a6ee697915ca8ec/Lib/pydoc.py#L288 (2认同)
  • 这似乎也能够正确处理“qualname”(不在模块名称空间顶部的对象)。 (2认同)
  • 已投赞成票。但奇怪的是这个函数位于“pydoc”模块中并且仍然没有文档记录。 (2认同)

Ada*_*nce 84

import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
Run Code Online (Sandbox Code Playgroud)

  • 一旦您动态导入模块,您就可以通过模块访问该类 (9认同)

Sta*_*tan 29

def import_class(cl):
    d = cl.rfind(".")
    classname = cl[d+1:len(cl)]
    m = __import__(cl[0:d], globals(), locals(), [classname])
    return getattr(m, classname)
Run Code Online (Sandbox Code Playgroud)

  • 这是干净的解决方案!您可以考虑使用:`(modulename,classname)= cl.rsplit('.',2)` (5认同)
  • @vdboor`rsplit('.',1)`? (4认同)

Jav*_*zzi 11

如果您使用的是Django,则可以使用它。是的,我知道OP并没有要求使用django,但是我遇到了这个问题,寻找Django解决方案,没有找到一个解决方案,并将其放在这里供下一个寻找它的下一个男孩/女孩使用。

# It's available for v1.7+
# https://github.com/django/django/blob/stable/1.7.x/django/utils/module_loading.py
from django.utils.module_loading import import_string

Klass = import_string('path.to.module.Klass')
func = import_string('path.to.module.func')
var = import_string('path.to.module.var')
Run Code Online (Sandbox Code Playgroud)

请记住,如果您要导入没有.re或的东西,请argparse使用:

re = __import__('re')
Run Code Online (Sandbox Code Playgroud)


Ric*_*ong 9

这里分享一些我就发现__import__importlib试图解决这个问题。

我正在使用 Python 3.7.3。

当我尝试d在模块中上课时a.b.c

mod = __import__('a.b.c')
Run Code Online (Sandbox Code Playgroud)

mod变量是指顶部的命名空间a

所以为了去上课d,我需要

mod = getattr(mod, 'b') #mod is now module b
mod = getattr(mod, 'c') #mod is now module c
mod = getattr(mod, 'd') #mod is now class d
Run Code Online (Sandbox Code Playgroud)

如果我们尝试做

mod = __import__('a.b.c')
d = getattr(mod, 'd')
Run Code Online (Sandbox Code Playgroud)

我们实际上是在寻找a.d.

使用时importlib,我想图书馆已经getattr为我们完成了递归。所以,当我们使用 时importlib.import_module,我们实际上得到了最深模块的句柄。

mod = importlib.import_module('a.b.c') #mod is module c
d = getattr(mod, 'd') #this is a.b.c.d
Run Code Online (Sandbox Code Playgroud)