原文:When to use __new__ in Python?
本文讨论Python中最被误解的__new__方法。 在创建类的实例时,会调用__init__和__new__方法。根据Python官方文档,__new__用于控制新实例的创建,而__init__用于控制新实例的初始化。在创建Python对象时,__new__是第一个调用的方法,它仅负责返回类的新实例。
相比之下,__init__不返回任何内容。它仅负责在创建实例后初始化实例。一个简单的经验法则是,除非你对不可变的Python数据类型(如数字数据类型、字符串、字节、冻结集和元组)进行子类,否则永远不应该重写__new___类。
在这一点上,你可能会想“Cool,阅读文档我也会。我什么时候真正需要使用这个?”假设你想创建一个返回小写字母的元组类型的元组。我们可以首先创建一个代理对象,该对象将所有参数小写,然后将其所有方法调用转发到基础元组。但是,这样做将显著降低性能,因为它是用Python编写的。而标准元组类型是用C编写的,使其比我们可以编写的任何Python代码都快得多。另一个天真但合理的想法是在__init__方法中将元组转为小写。
但是,此时对象已经存在于内存中,无法更改(不可变)。解决这个问题的唯一方法是在创建对象之前拦截和修改参数。这就是__new__发挥作用的地方。
class LowerCaseTuple(tuple):
def __new__(cls, iterable):
lower_iterable = (l.lower() for l in iterable)
return super().__new__(cls, lower_iterable)
print(LowerCaseTuple(['HELLO', 'MEDIUM']))
运行以上代码将产生一个小写元素的元组
('hello', 'medium')
现在之所以可以这样做,是因为__new__方法总是在__init_之前运行,因为它是类级静态方法。当通过调用类构造函数创建类的实例时,将首先调用__new__方法创建类实例,然后实例级别的__init__方法初始化类实例。需要注意的是,如果__new__不返回其绑定的类的实例(例如,小写字母的元组),它将完全跳过构造函数步骤,并将返回None。要在实际操作中看到这一点,只需注释掉__new__方法中的返回语句,然后运行脚本。