新式类的类型不是instance
,而是类本身。因此,这些PyInstance_*
方法对于新式类甚至没有意义。
事实上,文档明确解释了这一点:
请注意,此处描述的类对象代表旧式类,它将在 Python 3 中消失。为扩展模块创建新类型时,您将需要使用类型对象(类型对象部分)。
因此,您必须编写代码来检查klass
是旧式还是新式类,并为每种情况做适当的事情。旧式类的类型是PyClass_Type
,而新式类的类型要么是PyType_Type
,要么是自定义元类。
PyInstance_NewRaw
同时,对于新式类没有直接的等价物。或者,更确切地说,直接等价物——调用它的tp_alloc
槽然后添加一个字典——会给你一个非函数类。您可以尝试复制所有其他适当的工作,但这会很棘手。或者,您可以使用,但如果类(或其任何基类)中tp_new
有自定义函数,那将做错事。有关一些想法,请参阅#5180__new__
中被拒绝的补丁。
但实际上,您首先尝试做的可能不是一个好主意。也许如果您解释了为什么这是一项要求,以及您正在尝试做什么,那么会有更好的方法来做到这一点。
如果目标是通过创建一个新的未初始化的类实例来构建对象,然后_dict__
从初始化的原型复制它,那么我认为有一个更简单的解决方案对你有用:
__class__
是可写属性。所以(在 Python 中显示它;C API 基本相同,只是更冗长,我可能会在某处搞砸引用计数):
class NewStyleDummy(object):
pass
def make_instance(cls, instance_dict):
if isinstance(cls, types.ClassType):
obj = do_old_style_thing(cls)
else:
obj = NewStyleDummy()
obj.__class__ = cls
obj.__dict__ = instance_dict
return obj
新对象将是一个实例cls
——特别是,它将具有相同的类字典,包括 MRO、元类等。
cls
如果具有构造所需的元类,或自定义__new__
方法,或者……这将不起作用,__slots__
但是在这些情况下,您的复制设计__dict__
无论如何都没有任何意义。我相信,在任何情况下,任何事情都可能奏效,这个简单的解决方案会奏效。
呼叫cls.__new__
起初似乎是一个很好的解决方案,但实际上并非如此。让我解释一下背景。
当你这样做时:
foo = Foo(1, 2)
(哪里Foo
是新式类),它被转换成类似这样的伪代码:
foo = Foo.__new__(1, 2)
if isinstance(foo, Foo):
foo.__init__(1, 2)
问题是,如果Foo
它的基类或其中一个定义了一个__new__
方法,它将期望从构造函数调用中获取参数,就像__init__
方法一样。
__init__
正如您在问题中解释的那样,您不知道构造函数调用参数 - 实际上,这是您首先不能调用普通方法的主要原因。所以,你也不能打电话__new__
。
__new__
接受并忽略它给出的任何参数的基本实现。因此,如果您的所有类都没有__new__
override 或 a __metaclass__
,那么您将碰巧侥幸逃脱,因为其中有一个怪癖object.__new__
(顺便说一下,这是一个在 Python 3.x 中工作方式不同的怪癖)。但这些与以前的解决方案可以处理的情况完全相同,而且该解决方案的工作原理更明显。
换句话说:前面的解决方案依赖于没有人定义__new__
,因为它从不调用__new__
. 这个解决方案依赖于没有人定义__new__
,因为它__new__
使用错误的参数调用。