【问题标题】:Dynamic instantiation from string name of a class in dynamically imported module?从动态导入模块中类的字符串名称动态实例化?
【发布时间】:2021-06-25 20:22:34
【问题描述】:

在 python 中,我必须实例化某个类,知道它在字符串中的名称,但是这个类“存在于”动态导入的模块中。一个例子如下:

加载程序类脚本:

import sys
class loader:
  def __init__(self, module_name, class_name): # both args are strings
    try:
      __import__(module_name)
      modul = sys.modules[module_name]
      instance = modul.class_name() # obviously this doesn't works, here is my main problem!
    except ImportError:
       # manage import error

一些动态加载的模块脚本:

class myName:
  # etc...

我使用这种安排使加载程序类按照动态加载模块中的某些预定义行为使用任何动态加载模块...

【问题讨论】:

    标签: python


    【解决方案1】:

    你可以使用getattr

    getattr(module, class_name)
    

    访问类。更完整的代码:

    module = __import__(module_name)
    class_ = getattr(module, class_name)
    instance = class_()
    

    如below所述,我们可以使用 importlib

    import importlib
    module = importlib.import_module(module_name)
    class_ = getattr(module, class_name)
    instance = class_()
    

    【讨论】:

    • module = __import__(module, fromlist=[name]) 只为我工作。
    • 如果有人在使用上面提到的导入方法 Sven 时遇到问题,我发现我的代码使用以下方法效果更好,而不是 importlib.import_module。可以像这样使用:module = importlib.import_module(module_name)
    • @jpennell 您应该将其作为答案发布,能够直接使用obj.__module__ 返回的字符串通常更有用
    • importlib.import_module 将在需要时将 .py 文件加载到 pyc 中,并处理完整的 module.name.pathing.to.get.to.the 类。 __import__ 不会在 django 环境中做任何这些事情(在此之外未测试)
    【解决方案2】:

    tl;博士

    使用 importlib.import_module 导入根模块,并使用 getattr 函数按名称加载类:

    # Standard import
    import importlib
    # Load "module.submodule.MyClass"
    MyClass = getattr(importlib.import_module("module.submodule"), "MyClass")
    # Instantiate the class (pass arguments to the constructor, if needed)
    instance = MyClass()
    

    解释

    您可能不想使用 __import__ 按名称动态导入模块,因为它不允许您导入子模块:

    >>> mod = __import__("os.path")
    >>> mod.join
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'module' object has no attribute 'join'
    

    Here 是 python 文档关于 __import__ 的内容:

    注意:这是一个高级功能,日常用不到 Python 编程,不像 importlib.import_module()。

    相反,使用标准的importlib 模块按名称动态导入模块。使用getattr,您可以通过名称实例化一个类:

    import importlib
    my_module = importlib.import_module("module.submodule")
    MyClass = getattr(my_module, "MyClass")
    instance = MyClass()
    

    你也可以这样写:

    import importlib
    module_name, class_name = "module.submodule.MyClass".rsplit(".", 1)
    MyClass = getattr(importlib.import_module(module_name), class_name)
    instance = MyClass()
    

    此代码在 python ≥ 2.7(包括 python 3)中有效。

    【讨论】:

    • 也许我不明白你的回答,但我用过进口导入子模块:__import__("module." + submodule_name_string)
    • 以下代码导致 AttributeError:mod = __import__("os.path"); mod.join 而以下代码不会:mod = importlib.import_module("os.path"); mod.join
    • 哦,我明白了,你是对的,但我已经完成了以下操作来获取 os.path.join 方法:getattr(sys.modules["os.path"], "join")
    • 哈!我看到当使用它时,然后 '进口' 是不必要的 XD
    • 标记为答案的选项对我不起作用(使用子模块)但是这个答案确实如此。我认为这应该是答案,因为它是动态加载模块的更通用的解决方案。
    【解决方案3】:

    复制粘贴 SN-P:

    import importlib
    def str_to_class(module_name, class_name):
        """Return a class instance from a string reference"""
        try:
            module_ = importlib.import_module(module_name)
            try:
                class_ = getattr(module_, class_name)()
            except AttributeError:
                logging.error('Class does not exist')
        except ImportError:
            logging.error('Module does not exist')
        return class_ or None
    

    【讨论】:

      【解决方案4】:

      使用 getattr 从字符串中的名称获取属性。换句话说,将实例作为

      instance = getattr(modul, class_name)()
      

      【讨论】:

        【解决方案5】:

        可以简单地使用pydoc.locate 函数。

        from pydoc import locate
        my_class = locate("module.submodule.myclass")
        instance = my_class()
        

        【讨论】:

          【解决方案6】:

          如果你想让from foo.bar import foo2这句话动态加载,你应该这样做

          foo = __import__("foo")
          bar = getattr(foo,"bar")
          foo2 = getattr(bar,"foo2")
          
          instance = foo2()
          

          【讨论】:

            【解决方案7】:

            如果你想从字符串中导入一个类和方法,你应该这样做:

            dynamic_import 
            │   my_class.py
            │      
            └───subfolder
            │   │   my_subfolder_module.py
            │   │
            

            我的子文件夹模块.py

             class MySubfolderClass():
                    def test_method(self):
                        print ("Hello World")
            

            主程序

            import importlib 
              
            module = importlib.import_module('subfolder.my_subfolder_module')
            class_ = getattr(module, "MySubfolderClass")
            method_instance = getattr(class_(),"test_method")
            method_instance()
            #it will output the result of the test method, which is "Hello World"
            

            【讨论】:

              【解决方案8】:

              我无法从上面的示例中完全了解我的用例,但艾哈迈德让我最接近(谢谢)。对于将来阅读本文的人,这里是对我有用的代码。

              def get_class(fully_qualified_path, module_name, class_name, *instantiation):
                  """
                  Returns an instantiated class for the given string descriptors
                  :param fully_qualified_path: The path to the module eg("Utilities.Printer")
                  :param module_name: The module name eg("Printer")
                  :param class_name: The class name eg("ScreenPrinter")
                  :param instantiation: Any fields required to instantiate the class
                  :return: An instance of the class
                  """
                  p = __import__(fully_qualified_path)
                  m = getattr(p, module_name)
                  c = getattr(m, class_name)
                  instance = c(*instantiation)
                  return instance
              

              【讨论】:

                【解决方案9】:

                使用此 sn-p 代码:

                def to_class(path:str):
                    try:
                        from pydoc import locate
                        class_instance = locate(path)
                    except ImportError:
                        print('Module does not exist')
                    return class_instance or None
                

                用法:

                如果您的班级名称是 MyClass 并且位于 my_app.models.MyClass 那么:

                path = "my_app.models.MyClass"
                my_class = to_class(path)
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-11-15
                  • 1970-01-01
                  • 2017-08-11
                  相关资源
                  最近更新 更多