简体   繁体   English

在创建新的 `type` 实例(即类)时,类 `type.__init__` 和 `type.__new__` 做了什么?

[英]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.ctype_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.ctype_new_impl的步骤:

  • type_new_init() type_new_init()
  • type_new_set_attrs() type_new_set_attrs()
  • PyType_Ready() PyType_Ready()
  • fixup_slot_dispatchers() fixup_slot_dispatchers()
  • type_new_set_names() type_new_set_names()
  • type_new_init_subclass() type_new_init_subclass()

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM