何时(以及为啥)引入 Python `__new__()`?
Posted
技术标签:
【中文标题】何时(以及为啥)引入 Python `__new__()`?【英文标题】:When (and why) was Python `__new__()` introduced?何时(以及为什么)引入 Python `__new__()`? 【发布时间】:2019-09-21 19:41:09 【问题描述】:何时(以及为什么)引入 Python __new__()
函数?
创建一个类的实例分为三个步骤,例如MyClass()
:
MyClass.__call__()
被调用。此方法必须在MyClass
的元类中定义。
MyClass.__new__()
被调用(由__call__
)。在 MyClass
本身上定义。这将创建实例。
MyClass.__init__()
被调用(也被 __call__
调用)。这会初始化实例。
实例的创建可能会受到重载__call__
或__new__
的影响。通常没有理由重载__call__
而不是__new__
(例如Using the __call__ method of a metaclass instead of __new__?)。
我们有一些旧代码(仍然运行良好!)其中__call__
被重载。给出的原因是 __new__
当时不可用。所以我试图了解更多关于 Python 和我们的代码的历史,但我无法弄清楚 __new__
是什么时候引入的。
__new__
出现在documentation for Python 2.4 中,而不出现在Python 2.3 中,但它没有出现在任何 Python 2 版本的 whathsnew 中。我能找到的first commit that introduced __new__
(将descr-branch 合并回主干。)是从2001 年开始的,但是“返回主干”消息表明以前有过一些东西。几个月前的PEP 252 (Making Types Look More Like Classes) 和PEP 253 (Subtyping Built-in Types) 似乎是相关的。
了解更多关于 __new__
的介绍将让我们更多地了解 Python 为何如此。
编辑澄清:
似乎class.__new__
复制了metaclass.__call__
已经提供的功能。添加一个方法只是为了以更好的方式复制现有功能似乎不符合 Pythonic。
__new__
是为数不多的开箱即用的类方法之一(即使用cls
作为第一个参数),从而引入了以前不存在的复杂性。如果类是函数的第一个参数,那么可以认为该函数应该是元类的普通方法。但该方法确实已经存在:__call__()
。我觉得我错过了什么。
应该有一种——最好只有一种——明显的方法。
【问题讨论】:
您说引入__new__
违反了“应该有一种——最好只有一种——明显的方式来做到这一点”的规则。来自 Python 之禅。但还有一个:“虽然实用胜过纯洁。”。引入一种更易于编写、更易于阅读且不需要您编写元类的方法是一个实际的决定,它超越了“一种方法”的纯粹性。这就是为什么我可以说拥有__new__
是Pythonic。
这只是@Sanyash,'实用胜于纯粹'?我不太相信额外的复杂性实际上会使事情变得更加实用。 __new__
确实允许人们在不实际使用元类的情况下做元类的东西。不过不确定是否值得:当您使用__new__
时,您已经深入了解了,因此您不妨直接使用元类。不过你可能是对的。
@BlackShift:在答案中添加了对您的编辑的回复。
我看到the documentation of Python 2.2中提到的__new__
,是引入新式类的版本。
【参考方案1】:
这里就不解释__new__
的历史了,因为我从2005年开始才使用Python,所以是在它被引入语言之后。但这是其背后的基本原理。
新对象的正常配置方法是其类的__init__
方法。该对象已经被创建(通常通过间接调用object.__new__
)并且该方法只是初始化它。简单地说,如果你有一个真正不可变的对象,那就太晚了。
在那个用例中,Pythonic 方式是__new__
方法,它构建并返回新对象。它的好处是它仍然包含在类定义中并且不需要特定的元类。标准文档说明:
new() 主要用于允许不可变类型(如 int、str 或 tuple)的子类自定义实例创建。它通常在自定义元类中被覆盖以自定义类创建。
确实允许在元类上定义 __call__
方法,但恕我直言非 Pythonic,因为 __new__
应该足够了。此外,__init__
、__new__
和元类都深入挖掘了内部 Python 机制。所以规则应该是如果__init__
足够了就不要使用__new__
,如果__new__
足够了就不要使用元类。
【讨论】:
同意,现在应该使用__new__
而不是__call__
。不过,__new__
本身的引入似乎并不像 Python 那样。 “应该有一种——最好只有一种——明显的方式来做到这一点。”那么,如果 metaclass.__call__
已经符合要求,为什么还要添加 class.__new__
呢?例如。它是为数不多的开箱即用的静态方法之一(唯一一个?),从而增加了认知复杂性。【参考方案2】:
博文The Inside Story on New-Style Classes
(取自 http://python-history.blogspot.com
)由 Guido van Rossum
(Python 的 BDFL)编写,提供了有关此主题的一些很好的信息。
一些相关的引用:
新式类引入了一个新的类方法
__new__()
,它可以让 类作者自定义如何创建新类实例。经过 重写__new__()
类作者可以实现类似的模式 单例模式,返回以前创建的实例(例如,从 空闲列表),或返回不同类的实例(例如, 子类)。但是,__new__
的使用还有其他重要的 应用程序。例如,在 pickle 模块中,__new__
用于 在反序列化对象时创建实例。在这种情况下,实例 已创建,但未调用__init__
方法。
__new__
的另一个用途是帮助继承 immutable 类型。由于它们的不变性,这些类型的对象可以 不能通过标准的__init__()
方法初始化。相反,任何 必须像对象一样执行某种特殊的初始化 创建;例如,如果类想要修改值 存储在不可变对象中,__new__
方法可以通过 将修改后的值传递给基类__new__
方法。
您可以阅读整篇文章以获取有关此主题的更多信息。
另一篇关于New-style Classes
的帖子与上面引用的帖子一起写了一些额外的信息。
编辑:
作为对 OP 的编辑和 Python Zen 的引述的回应,我会这样说。Zen of Python 不是由该语言的创建者而是由 Tim Peters 编写的,并且仅在 2004 年 8 月 19 日发布. 我们必须考虑到 __new__
只出现在 Python 2.4 的文档中(在 November 30, 2004 上发布),而且这个特定的指南(或格言)甚至不存在公开 当__new__
被引入语言时。
即使以前非正式地存在过这样的指南文档,我不认为作者打算将它们误解为整个语言和生态系统的设计文档。
【讨论】:
这些帖子虽然很有见地,但并没有真正说明为什么在metaclass.__call__
已经提供了博客中描述的所有功能时引入了 class.__new__
。引入一种全新的方法只是为了以稍微更好的方式复制现有功能似乎不符合 Pythonic。我觉得我从图片中遗漏了一些东西。
@BlackShift:帖子只提到了__new__
的三个用例。根据帖子,还有其他未列出的重要应用程序。你说它是非 Python 的,因为在 Python 中完成事情的方法不应该超过一种?
感谢@P.W 的编辑,那个时间线和@Sanyash 的评论很好地解释了__new__
的引入。我希望__new__
会有一些新功能,例如__call__
无法完成的事情,我还没有意识到。以上是关于何时(以及为啥)引入 Python `__new__()`?的主要内容,如果未能解决你的问题,请参考以下文章
python如何决定何时按值传递参数以及何时按引用传递参数? [复制]
Python new 类方法和 init 实例方法以及单例模式的简单讨论
为啥 __new__ 和 __init__ 在指向超类时表现不同?