[英]What does class `type.__init__` and `type.__new__` do when creating a new `type` instance(i.e. class)?
As is known to all, there are many jobs are done behind the scenes when creating a new class
in Python, such as setting attributes like __dict__
, __class__
, __metaclass__
, etc.众所周知,在 Python 中创建新
class
时,有很多工作是在幕后完成的,例如设置属性,如__dict__
、 __class__
、 __metaclass__
等。
I know that when creating a new class, the type.__new__
method will be called, and type.__init__
will also be called on condition that call.__new__
returns a instance of type
.我知道在创建新类时,会调用
type.__new__
方法,并且type.__init__
也会在call.__new__
返回type
的实例的情况下被调用。 So I guess these two methods might be in charge of some of the work, but I cannot find any description in docs about their real function.所以我想这两种方法可能负责一些工作,但我在文档中找不到任何关于它们真正功能的描述。 My question is, what exactly do these two methods do for making a
class
?我的问题是,这两种方法究竟对创建
class
有什么作用?
EDIT:编辑:
I know what a metaclass is and what roughly metaclass does in the process of creating a type
instance, but I am wondering about how these two methods cooperate to achieve the job of creating a type
instance.我知道元类是什么,以及元类在创建
type
实例的过程中大致做了什么,但我想知道这两种方法如何协同完成创建type
实例的工作。 Maybe @BrenBarn is right that this is implementation related.也许@BrenBarn 是正确的,这是与实现相关的。 And I just want to make sure about that.
我只是想确定一下。 For example, if I rewrite the
__new__
method in my own metaclass T
, and return type(clsname, bases, dct)
directly instead of calling __new__
in base class type
as what people usually do, then neither T.__init__
nor type.__init__
will be called, since returned object is not an instance of T
.例如,如果我在自己的元类
T
中重写__new__
方法,并直接返回type(clsname, bases, dct)
而不是像人们通常所做的那样在基类type
中调用__new__
,那么T.__init__
和type.__init__
都不会被调用,因为返回的对象不是T
的实例。 If so, what am I expecting to miss owing to the absense of __init__
?如果是这样,由于缺少
__init__
,我会错过什么? And also, can I expect that to be a consistent behavior across various implementations?而且,我是否可以期望这是跨各种实现的一致行为?
am wondering about how these two methods cooperate to achieve the job of creating a type instance
我想知道这两种方法如何协作来完成创建类型实例的工作
The type.__init__
method is only responsible for checking that there are 1 or 3 arguments and that there are no keyword arguments. type.__init__
方法只负责检查是否有 1 个或 3 个参数以及是否没有关键字参数。 The C source code for this is in the type_init()
function in Objects/typeobject.c
. C 源代码在
Objects/typeobject.c
的type_init()
函数中。
The type.__new__
method does all of the rest work in creating a new class. type.__new__
方法完成了创建新类的所有其余工作。 Here are the steps for type_new_impl
in Objects/typeobject.c
:以下是
Objects/typeobject.c
中type_new_impl
的步骤:
It matters a lot whether you implement these calls in Python or in C. For example the call type(clsname, bases, dct)
is the combination of tp_new
and tp_init
, but will call that __init__
if and only if it was setup as the tp_init
pointer on the metaclass of the newly created type, not if that merely has an __init__
in its __dict__
. 这是相当重要的,你是否在Python中实现或C.例如调用这些呼叫
type(clsname, bases, dct)
是组合tp_new
和tp_init
,但会调用__init__
当且仅当它设置为tp_init
新创建类型的元类上的指针,如果该指针仅在其__dict__
具有__init__
,则不可以。 So, you have access to that from C, not from Python. 因此,您可以通过C而不是Python进行访问。 Of course you are free to call
__init__
explicitly yourself. 当然,您可以自己显式调用
__init__
。
Similar problems exist in type_new
for most of type slots, __init__
among them: for performance reasons, even though they are strings in the lookup, selection is on the pointer to the strings, so the call to __init__
only works if you use of interned strings in your implementation of tp_new
. 对于大多数类型的插槽,其中的
__init__
,在type_new
也存在类似的问题:出于性能原因,即使它们是查找中的字符串,但选择type_new
指向字符串的指针上,因此,仅当您使用插入的字符串时,对__init__
的调用才有效在您的tp_new
实施中。 And yes, this behavior has subtly changed across Python versions, and in the case of 2.7, even across patch versions. 是的,这种行为在Python版本之间,甚至在2.7版本,甚至在补丁版本之间,都发生了微妙的变化。
I'm pretty sure that these subtle problems are the reason why you cannot find much about them in the docs: if the behavior were documented, it would have to be supported, which would come at a performance cost for the common case where there is no user-provided tp_new
. 我非常确定,这些细微的问题是为什么您无法在文档中找到太多有关这些问题的原因:如果记录了该行为,则必须对其进行支持,而对于存在这种情况的常见情况,这将以性能为代价没有用户提供的
tp_new
。
As for the reason to have an __init__
called on freshly created type: it is only important if you have extra data members to initialize that can not be part of the dictionary handed to the constructor (the normal type_new
will take care of those.) This is often the case when defining the new type in C, as the type may carry internal data that is not supposed to be python-visible, but uncommon when done from Python. 至于在新创建的类型上调用
__init__
的原因:仅当您有额外的数据成员要初始化而不能作为传递给构造函数的字典的一部分时, type_new
(正常的type_new
会处理这些问题。)在C中定义新类型时经常会遇到这种情况,因为该类型可能承载内部数据,这些内部数据本来不应该是python可见的,但是用Python处理时并不常见。
As a perhaps extreme example, consider Python-C++ cross-inheritance in cppyy ( http://cppyy.readthedocs.io/ ). 作为一个极端的例子,考虑cppyy( http://cppyy.readthedocs.io/ )中的Python-C ++交叉继承。 All C++ proxy classes have an associated metaclass.
所有C ++代理类都有一个关联的元类。 When a Python class is derived from a C++ class and overrides C++ virtual methods, the metaclass gets called and it interjects a dispatcher class that is invisible to the Python-side: it is part of the C++ hierarchy, but not part of the Python hierarchy.
当从C ++类派生Python类并覆盖C ++虚拟方法时,元类将被调用,并插入Python端不可见的调度程序类:它是C ++层次结构的一部分,但不是Python层次结构的一部分。 This extra setup is performed after the normal type_new has run.
在正常的type_new运行之后执行此额外的设置。 (As an implementation detail, this is still all done in the tp_new method, to avoid problems with when tp_init is called and when not.) Note here that this behavior requires a hook when a derived class is created, which can not be done without a metaclass.
(作为实现的详细信息,这仍然全部在tp_new方法中完成,以避免出现何时调用tp_init以及何时不调用tp_init的问题。)请注意,此行为在创建派生类时需要一个钩子,如果没有该类,则无法完成元类。
In general the Python-side code should, however, behave the same across implementations and not show the subtle problems of the C side. 但是,一般而言,Python端代码在各个实现中的行为应相同,并且不会显示C端的细微问题。 But underlying, there may be large differences, not just the subtle ones alluded to above.
但潜在的差异可能很大,而不仅仅是上面提到的细微差别。 Eg PyPy does not support metaclasses in RPython at all, so in that case you have to embed Python code.
例如,PyPy根本不支持RPython中的元类,因此在这种情况下,您必须嵌入Python代码。
Personally, on the Python side, I have far less need for __new__
. 就个人而言,在Python方面,我对
__new__
需求要少__new__
。 In practice, it's a bit of a misnomer: if you just want to control the class creation and nothing more, implementing __call__
on a pretend metaclass provides the same end-user syntax with far less hassle. 实际上,这有点用词不当:如果您只想控制类的创建,仅此而已,那么在假装元类上实现
__call__
可以提供相同的最终用户语法,而麻烦却少得多。 The only reason to use __new__
is if you want to control its behavior post-creation through its custom metaclass methods/properties. 使用
__new__
的唯一原因是,如果要通过其自定义元类方法/属性控制其创建后的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.