【问题标题】:How to dynamically load a Python class如何动态加载 Python 类
【发布时间】:2010-10-07 13:32:16
【问题描述】:

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

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

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

my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()

【问题讨论】:

  • 我还需要能够将类引用分配给变量。
  • 这似乎与以下内容重复:stackoverflow.com/questions/452969/…
  • 你说得对,它是重复的,谢谢你找到它
  • @JohnTyree 动态加载类如何意味着程序不有趣?你能举个例子,让这种批评对 SO 成员更有用吗?
  • 有趣“够了”。这只是一种半开玩笑的说法,有时因为您无法控制且难以预测的因素而需要做一些奇怪的事情。之前的评论基本上是说,“你为什么不直接导入正常的方式?”我是说他们有一天也会有一个奇怪的角落案例,需要做一些丑陋的事情。

标签: python reflection python-import


【解决方案1】:
module = __import__("my_package/my_module")
the_class = getattr(module, "MyClass")
obj = the_class()

【讨论】:

【解决方案2】:

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

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

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

__import__('foo.bar.baz.qux')

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

my_import('foo.bar.baz.qux')

或者在你的例子中:

klass = my_import('my_package.my_module.my_class')
some_object = klass()

编辑:我对此有点不理解。您基本上想要做的是:

from my_package.my_module import my_class

仅当您有一个 empty fromlist 时才需要上述功能。因此,适当的调用应该是这样的:

mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')

【讨论】:

  • 我试过 my_import('my_package.my_module.my_class') 但没有找到模块 my_class,这是有道理的,因为它是一个类而不是一个模块。但是,如果我可以在调用 my_import 后使用 gettattr 获取课程
  • 这很奇怪。使用 getattr 调用第一个点之后的所有内容。应该没有什么区别。
  • 谢谢,我认为这是最好的方法。现在我只需要将字符串“my_pakcage.my_module.my_class”拆分为 mod_name、klass_name 的最佳方法,但我想我可以弄清楚 :)
  • importpython 文档(关于代码)说要使用 importlib。 Adam Spence 的结帐答案也应该如此
【解决方案3】:
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)

【讨论】:

  • 这是干净的解决方案!您可以考虑使用:(modulename, classname) = cl.rsplit('.', 2)
  • 太棒了)我用不同的工具创建了 putils 包,还在那里导入了类。如果你愿意,你可以从那个包中使用它。
  • @vdboor rsplit('.', 1) ?
  • 我设法通过了 {} 而不是 globals/locals,它仍然可以正常工作
【解决方案4】:
import importlib

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

【讨论】:

  • 动态导入模块后,您可以通过模块访问类
  • 刚刚编辑了我的答案以更简洁。这是在 Python 中加载类的最佳方式。
  • B-B-B-本尼和斯宾塞! ;)
  • 太棒了!它甚至是 2.7 及更高版本标准库的一部分。
  • 这是访问模块/类的正确方法。文档在这里声明:docs.python.org/3/library/importlib.html#importlib.__import__
【解决方案5】:

如果您不想自己动手,pydoc 模块中有一个功能可以做到这一点:

from pydoc import locate
my_class = locate('my_package.my_module.MyClass')

与此处列出的其他方法相比,这种方法的优势在于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

它依赖于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)."""

【讨论】:

  • 我赞成这个答案。顺便说一句,这里是也有 safeimport 的代码,因为为此导入 pydoc 似乎很奇怪:github.com/python/cpython/blob/…
  • 我也赞成这个答案,这是所有相关答案中最好的。
  • 这似乎也能够正确处理qualname(对象不在模块命名空间的顶部)。
  • 其实是最好的答案
  • 有点题外话,但似乎这应该与pydoc不同。
【解决方案6】:

在 Google App Engine 中有一个名为 import_stringwebapp2 函数。更多信息请看这里:https://webapp-improved.appspot.com/api/webapp2.html

所以,

import webapp2
my_class = webapp2.import_string('my_package.my_module.MyClass')

例如,这用于webapp2.Route,您可以在其中使用处理程序或字符串。

【讨论】:

    【解决方案7】:

    好的,对我来说这就是它的工作方式(我使用的是 Python 2.7):

    a = __import__('file_to_import', globals(), locals(), ['*'], -1)
    b = a.MyClass()
    

    那么,b 是类 'MyClass' 的一个实例

    【讨论】:

      【解决方案8】:

      如果你碰巧已经有一个你想要的类的实例,你可以使用'type'函数来提取它的类类型并用它来构造一个新的实例:

      class Something(object):
          def __init__(self, name):
              self.name = name
          def display(self):
              print(self.name)
      
      one = Something("one")
      one.display()
      cls = type(one)
      two = cls("two")
      two.display()
      

      【讨论】:

        【解决方案9】:

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

        # 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')
        

        请记住,如果您想导入没有. 的内容,例如reargparse,请使用:

        re = __import__('re')
        

        Here are the the official docs for import_string.

        【讨论】:

          【解决方案10】:

          在此分享我在尝试解决此问题时在__import__importlib 上发现的一些内容。

          我正在使用 Python 3.7.3。

          当我尝试进入模块 a.b.c 中的课程 d 时,

          mod = __import__('a.b.c')
          

          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
          

          如果我们尝试这样做

          mod = __import__('a.b.c')
          d = getattr(mod, 'd')
          

          我们实际上是在寻找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
          

          【讨论】:

            【解决方案11】:

            Python 有一个内置库 importlib 来完成这项工作。 :,如何绕过包名作为参数动态访问模块方法和类方法。下面给出一个例子。

            模块 1:

            def get_scenario_data():
                return "module1 scenario data"
            
            
            class Module1:
            
                def module1_function1(self):
                    return "module1_function"
            
                def module1_function2(self):
                    return "module2_function"
            

            模块 2:

            def get_scenario_data():
                return "module2 scenario data"
            
            
            
            class Module2:
            
                def module2_function1(self):
                    return "module2_function1"
            
                def module2_function2(self):
                    return "module2_function2"
            

            模块测试:

            1. 将根据包名作为参数动态访问模块方法
            2. 将根据包名作为参数动态访问类方法。

            模块测试

            import importlib
            
            module = importlib.import_module('pack1.nestedpack1.module1')
            print(module.get_scenario_data())
            modul1_cls_obj = getattr(module, 'Module1')()
            print(modul1_cls_obj.module1_function1())
            print(modul1_cls_obj.module1_function2())
            
            module = importlib.import_module('pack1.nestedpack1.module2')
            modul2_cls_obj = getattr(module, 'Module2')()
            print(modul2_cls_obj.module2_function1())
            print(modul2_cls_obj.module2_function2())
            print(module.get_scenario_data())
            

            结果

            module1 scenario data
            module1_function
            module2_function
            module2_function1
            module2_function2
            module2 scenario data
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2023-03-30
              • 1970-01-01
              • 2017-08-05
              • 1970-01-01
              • 2013-11-10
              • 1970-01-01
              • 2011-04-04
              • 2014-09-27
              相关资源
              最近更新 更多