如何在Python中从类对象创建新实例

104

我需要在Python中动态创建一个类的实例。基本上我使用了load_module和inspect模块来导入和加载类到一个类对象中,但是我无法弄清如何创建这个类对象的实例。


10
什么?instance = Class()...(注:这是一行Python代码,意思是创建一个类的实例对象并将其赋值给名为"instance"的变量。) - Jochen Ritzel
1
我猜你想动态创建一个新类,而不是给定类的对象。 - jsbueno
新实例意味着在内存中具有自己的空间的新对象。大多数语言使用“new”关键字,以便明确表明对象是新的,并且新实例(对象)的属性不与另一个实例共享/引用。 - Bill Rosmus
由于Python不使用“new”关键字,因此我在下面更新了我的答案,并对类实例化机制进行了全面解释。 - jsbueno
6个回答

176

我找到了答案,解决了我来到这个页面的问题。由于没有人提供我想要的答案,所以我决定发布出来。

class k:
  pass

a = k()
k2 = a.__class__
a2 = k2()

此时,a和a2都是同一个类(k类)的实例。


4
这似乎是正确的答案。有效且简单易懂。 - Miguel Vazq
1
注意,如果__init__接受参数,您还可以将参数传递给构造函数。 a3 = k2(7) 将其翻译为: a3 = k2(7) - gerardw
k2 = a.__class__k2 = k 有什么区别? - Joshua Coady
3
k2 是对象类型,在这种情况下是 a 的类型 (a.__class),k2() 创建该类型的新对象实例,与 a 独立。k2 = k 只是创建了对相同实例的新引用。 - pixelou
6
你也可以使用 k2 = type(a) 获取类对象,然后调用该类对象 a2 = k2()。这本质上是相同的,但你不必在代码中使用那些丑陋的双下划线... 这样可能更具可移植性。 - robert

26

只需要使用三个参数调用内置的"type"函数,就像这样:

ClassName = type("ClassName", (Base1, Base2,...), classdictionary)

更新:如下评论所述,这并不是完全回答此问题的答案。我将保留它未删除,因为有一些人会尝试动态创建类 - 这就是上面一行代码的作用。

要创建一个已经有引用的类的对象,按照被接受的答案所述,只需要调用该类即可:

instance = ClassObject()

Python的实例化机制如下:

Python没有像其他某些编程语言一样使用new关键字,而是通过其数据模型解释了创建类实例的机制。当按照与任何其他可调用对象相同的语法调用它时,将调用其类的__call__方法(在类的情况下,其类是“元类”,通常为内置type)。这个调用的普通行为是调用被实例化的类上的(伪)静态__new__方法,然后调用其__init__方法。 __new__方法负责分配内存等任务,并通常由object__new__完成,object是类层次结构根。

因此,调用ClassObject()会调用ClassObject.__class__.call()(通常将是type.__call__),此__call__方法将接收ClassObject本身作为第一个参数 - 纯Python实现应该是这样的:(当然,cPython版本是用C编写的,并且有很多额外的代码来处理特殊情况和优化)

class type:
    ...
    def __call__(cls, *args, **kw):
          constructor = getattr(cls, "__new__")
          instance = constructor(cls) if constructor is object.__new__ else constructor(cls, *args, **kw)
          instance.__init__(cls, *args, **kw)
          return instance

(我不记得在文档中看到过关于抑制额外参数传递给根__new__,并将其传递给其他类的确切理由(或机制) - 但这就是在“现实生活”中发生的情况 - 如果使用任何额外参数调用object.__new__,它会引发类型错误 - 然而,任何自定义的__new__实现通常会获得额外参数)


9
OP不是想创建一个实例而不是一个类吗? - Keith

3
这是如何在你的代码中动态创建一个名为Child的类,假设已经存在Parent...即使你没有显式的Parent类,你也可以使用object...
下面的代码定义了__init__(),然后将其与类相关联。
>>> child_name = "Child"
>>> child_parents = (Parent,)
>>> child body = """
def __init__(self, arg1):
    # Initialization for the Child class
    self.foo = do_something(arg1)
"""
>>> child_dict = {}
>>> exec(child_body, globals(), child_dict)
>>> childobj = type(child_name, child_parents, child_dict)
>>> childobj.__name__
'Child'
>>> childobj.__bases__
(<type 'object'>,)
>>> # Instantiating the new Child object...
>>> childinst = childobj()
>>> childinst
<__main__.Child object at 0x1c91710>
>>>

2
如果你有一个包含类的模块需要导入,可以像这样操作。
module = __import__(filename)
instance = module.MyClass()

如果您不知道类的名称,可以通过迭代模块中可用的类来获取。
import inspect
module = __import__(filename)
for c in module.__dict__.values():
    if inspect.isclass(c):
        # You may need do some additional checking to ensure 
        # it's the class you want
        instance = c()

1

我认为最好的方法是使用type。这里有一个例子:

>>> class Foo:
...     def __init__(self, s):
...       self.s = s
... 
>>> a = Foo("hello")
>>> a.s
'hello'
>>> b = type(a)("world")
>>> b.s
'world'
>>> assert isinstance(a, Foo)
>>> assert isinstance(b, Foo)

b 是一个与 a 类型相同的实例。


1
如果您有某个类对象,只需调用它(带括号)即可实例化该对象。
class MyClass: pass        
some_class = MyClass    
some_instance = some_class() # -> instance of MyClass

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