繁体   English   中英

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

[英]What does class `type.__init__` and `type.__new__` do when creating a new `type` instance(i.e. class)?

众所周知,在 Python 中创建新class时,有很多工作是在幕后完成的,例如设置属性,如__dict____class____metaclass__等。

我知道在创建新类时,会调用type.__new__方法,并且type.__init__也会在call.__new__返回type的实例的情况下被调用。 所以我想这两种方法可能负责一些工作,但我在文档中找不到任何关于它们真正功能的描述。 我的问题是,这两种方法究竟对创建class有什么作用?

编辑:

我知道元类是什么,以及元类在创建type实例的过程中大致做了什么,但我想知道这两种方法如何协同完成创建type实例的工作。 也许@BrenBarn 是正确的,这是与实现相关的。 我只是想确定一下。 例如,如果我在自己的元类T中重写__new__方法,并直接返回type(clsname, bases, dct)而不是像人们通常所做的那样在基类type中调用__new__ ,那么T.__init__type.__init__都不会被调用,因为返回的对象不是T的实例。 如果是这样,由于缺少__init__ ,我会错过什么? 而且,我是否可以期望这是跨各种实现的一致行为?

我想知道这两种方法如何协作来完成创建类型实例的工作

type.__init__方法只负责检查是否有 1 个或 3 个参数以及是否没有关键字参数。 C 源代码在Objects/typeobject.ctype_init()函数中。

type.__new__方法完成了创建新类的所有其余工作。 以下是Objects/typeobject.ctype_new_impl的步骤:

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

这是相当重要的,你是否在Python中实现或C.例如调用这些呼叫type(clsname, bases, dct)是组合tp_newtp_init ,但会调用__init__当且仅当它设置为tp_init新创建类型的元类上的指针,如果该指针仅在其__dict__具有__init__ ,则不可以。 因此,您可以通过C而不是Python进行访问。 当然,您可以自己显式调用__init__

对于大多数类型的插槽,其中的__init__ ,在type_new也存在类似的问题:出于性能原因,即使它们是查找中的字符串,但选择type_new指向字符串的指针上,因此,仅当您使用插入的字符串时,对__init__的调用才有效在您的tp_new实施中。 是的,这种行为在Python版本之间,甚至在2.7版本,甚至在补丁版本之间,都发生了微妙的变化。

我非常确定,这些细微的问题是为什么您无法在文档中找到太多有关这些问题的原因:如果记录了该行为,则必须对其进行支持,而对于存在这种情况的常见情况,这将以性能为代价没有用户提供的tp_new

至于在新创建的类型上调用__init__的原因:仅当您有额外的数据成员要初始化而不能作为传递给构造函数的字典的一部分时, type_new (正常的type_new会处理这些问题。)在C中定义新类型时经常会遇到这种情况,因为该类型可能承载内部数据,这些内部数据本来不应该是python可见的,但是用Python处理时并不常见。

作为一个极端的例子,考虑cppyy( http://cppyy.readthedocs.io/ )中的Python-C ++交叉继承。 所有C ++代理类都有一个关联的元类。 当从C ++类派生Python类并覆盖C ++虚拟方法时,元类将被调用,并插入Python端不可见的调度程序类:它是C ++层次结构的一部分,但不是Python层次结构的一部分。 在正常的type_new运行之后执行此额外的设置。 (作为实现的详细信息,这仍然全部在tp_new方法中完成,以避免出现何时调用tp_init以及何时不调用tp_init的问题。)请注意,此行为在创建派生类时需要一个钩子,如果没有该类,则无法完成元类。

但是,一般而言,Python端代码在各个实现中的行为应相同,并且不会显示C端的细微问题。 但潜在的差异可能很大,而不仅仅是上面提到的细微差别。 例如,PyPy根本不支持RPython中的元类,因此在这种情况下,您必须嵌入Python代码。

就个人而言,在Python方面,我对__new__需求要少__new__ 实际上,这有点用词不当:如果您只想控制类的创建,仅此而已,那么在假装元类上实现__call__可以提供相同的最终用户语法,而麻烦却少得多。 使用__new__的唯一原因是,如果要通过其自定义元类方法/属性控制其创建后的行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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