从动态导入的模块中的类的字符串名称动态实例化?


179

在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...

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

Answers:


249

您可以使用getattr

getattr(module, class_name)

访问课程。更完整的代码:

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

如前所述下面,我们可以使用导入库

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

3
module = __import__(module, fromlist=[name])只为我工作。
umpirsky

16
如果有人遇到上面提到的导入方法Sven的麻烦,我发现我的代码使用以下方法代替importlib.import_module可以更好地工作。可以这样使用:module = importlib.import_module(module_name)
jpennell

@jpennell,您应该发布它作为答案,通常能够更直接地使用obj.__module__
Anentropic 2014年

importlib.import_module会在需要时将.py文件加载到pyc中,并处理完整的module.name.pathing.to.get.to.class。__import__不会在django环境中做任何事情(未经测试)
James

122

tl; dr

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'

是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)中有效。


1
也许我不明白您的答案,但是我已经使用import导入了子模块:__import __(“ module。” + submodule_name_string)
Javier Novoa C.

以下代码产生一个AttributeError:mod = __import__("os.path"); mod.join虽然下面的犯规:mod = importlib.import_module("os.path"); mod.join
雷吉斯B.

哦,我知道,您是对的,但我已完成以下操作以获取os.path.join方法:getattr(sys.modules [“ os.path”],“ join”)
哈维尔·诺沃阿C.

哈!并且我发现使用该代码时,不需要使用“ 导入 ” XD
Javier Novoa C.15.7.11

2
标记为答案的选项对我不起作用(使用子模块),但是此答案有效。我认为这应该是答案,因为它是用于动态加载模块的更通用的解决方案。
rkachach

14

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

instance = getattr(modul, class_name)()

10

复制粘贴代码段:

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

一个人可以简单地使用该pydoc.locate功能。

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

2

在上面的示例中,我无法完全达到用例的目的,但是艾哈迈德让我最接近了(谢谢)。对于以后阅读此书的人来说,以下是对我有用的代码。

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.