如何在Python中动态创建类的实例?

75

我有一组类名,并希望动态创建它们的实例。例如:

names=[
'foo.baa.a',
'foo.daa.c',
'foo.AA',
 ....
]

def save(cName, argument):
 aa = create_instance(cName) # how to do it?
 aa.save(argument)

save(random_from(names), arg)

如何在Python中动态创建这些实例?谢谢!

7个回答

102

假设你已经使用类似以下的方式导入了相关的类

from [app].models import *

你需要做的就是

klass = globals()["class_name"]
instance = klass()

3
很好的回答。 - H S Rathore
4
你为什么把它命名为“klass”?这个词有特殊的意义吗? - Nyxynyx
适用于Django模型。 - Kermit
7
请参考编程中“保留字”的概念;通常要避免使用在所用语言和库中作为对象和函数名称常见的单词。 - Kermit
在这里假设...这要求类已经存在,而我想定义一个具有动态名称的新类。 - sam-6174

27

1
其中一些示例使用 __import__,对于更新的代码,您可以切换到 importlib http://docs.python.org/dev/library/importlib.html。 (py>=2.7 | >=3.1) - SiggyF
15
请勿使用链接,提供真实的描述。 - shadowbq

20

这对我有用:

from importlib import import_module

class_str: str = 'A.B.YourClass'
try:
    module_path, class_name = class_str.rsplit('.', 1)
    module = import_module(module_path)
    return getattr(module, class_name)
except (ImportError, AttributeError) as e:
    raise ImportError(class_str)

3
对我来说非常好用!谢谢!! - Mike Gordo
7
是的,使用importlib来解析类比手动处理星号导入等反模式要好得多。这个答案是截至Python 3.3时最好的解决方案。 - Arne

10
您可以使用Python内置的eval()语句来实例化您的类。就像这样:
aa = eval(cName)()

注意!

使用eval是非常危险的,是许多基于代码注入的安全风险的关键。


7
不要使用eval,因为它容易受到安全问题的影响。 - Necrolyte2
2
它有什么漏洞吗? - Ujjaval Moradiya
2
根据上下文而定,如果cName来自用户的某些输入,那么它是危险的,因为黑客可以在其中注入Python代码:这是真的,但很多时候你只需要从文本文件或仅由开发人员/管理员控制的环境变量设置应用程序,这种情况下足够安全和简单。 - Mariano Ruiz
1
安全问题存在,即使不使用“eval”,攻击者也可以实例化任何想要的类。我的观点是:应进一步分析此实现要求以确保安全。 - Tomer W

8
您可以经常完全避免字符串处理部分。
import foo.baa 
import foo.AA
import foo

classes = [ foo.baa.a, foo.daa.c, foo.AA ]

def save(theClass, argument):
   aa = theClass()
   aa.save(argument)

save(random.choice(classes), arg)

请注意,我们不会使用类名称的字符串表示形式。
在Python中,您可以直接使用类本身。

假设数组来自数据库。因此,这个解决方案并没有帮助。 - Amir Shabani
1
你只需要执行 type(cName)(**arguments) - KIC

3

我的问题是,我希望在命令行中使用字符串指定参数将其传递到__init__中。例如,相当于

import a.b.ClassB as ClassB
instance = ClassB.ClassB('World')

命令行上的字符串是"a.b.ClassB.ClassB('World')"

以下是模块a.b.ClassB中的类

class ClassB():

    def __init__(self, name:str):
        self._name = name

    def hello(self):
        print("Hello " + self._name + "!")

我们可以使用以下方式创建这个类:

import importlib

def create_instance(class_str:str):
    """
    Create a class instance from a full path to a class constructor
    :param class_str: module name plus '.' plus class name and optional parens with arguments for the class's
        __init__() method. For example, "a.b.ClassB.ClassB('World')"
    :return: an instance of the class specified.
    """
    try:
        if "(" in class_str:
            full_class_name, args = class_name = class_str.rsplit('(', 1)
            args = '(' + args
        else:
            full_class_name = class_str
            args = ()
        # Get the class object
        module_path, _, class_name = full_class_name.rpartition('.')
        mod = importlib.import_module(module_path)
        klazz = getattr(mod, class_name)
        # Alias the the class so its constructor can be called, see the following link.
        # See https://www.programiz.com/python-programming/methods/built-in/eval
        alias = class_name + "Alias"
        instance = eval(alias + args, { alias: klazz})
        return instance
    except (ImportError, AttributeError) as e:
        raise ImportError(class_str)

if __name__ == "__main__":
    instance = create_instance("a.b.ClassB.ClassB('World')")
    instance.hello()

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接