在Python中,__new__
用于初始化不可变类型,__init__
通常初始化可变类型。如果从语言中删除了__init__
,那么就不能再轻松完成了什么?
例如,
class A:
def __init__(self, *, x, **kwargs):
super().__init__(**kwargs)
self.x = x
class B(A):
def __init__(self, y=2, **kwargs):
super().__init__(**kwargs)
self.y = y
可以使用__new__
重写:
class A_N:
def __new__(cls, *, x, **kwargs):
obj = super().__new__(cls, **kwargs)
obj.x = x
return obj
class B_N(A_N):
def __new__(cls, y=2, **kwargs):
obj = super().__new__(cls, **kwargs)
obj.y = y
return obj
对问题范围的澄清:这不是关于如何使用__init__
和__new__
或者它们之间有什么区别的问题。这是一个关于如果从语言中删除__init__
会发生什么的问题。什么事情会破裂?有什么事情会变得更难或不可能吗?
答案 0 :(得分:19)
在解释缺失的功能之前,让我们回到__new__
和__init__
的定义:
__ new __ 是实例创建的第一步。它首先被调用,负责返回您班级的新实例。
然而, __ init__不会返回任何内容;它只负责 在创建实例后初始化实例。
主要是你会在灵活性方面松懈。你会得到很多语义上的头痛和初始化和构造的松散分离(通过加入__new__ and
init ,我们将把构造和初始化加入到一步......)。
我们来看看下面的代码段:
class A(object):
some_property = 'some_value'
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
obj.some_property = cls.some_property
return obj
class B(A):
some_property = 2
def __new__(cls, *args, **kwargs):
obj = super(B, cls).__new__(cls)
return obj
将__init__
行动移至__new__
:
在B
之前初始化A
:当您使用__new__
方法而不是__init__
时,创建B新实例的第一步是调用{{1}作为副作用,您无法在A.__new__
初始化之前初始化B
(访问并为新B实例分配一些属性)。使用 A
可以提供这种灵活性。
对初始化顺序失去控制:让我们假设您从两个类(__init__
,B_N
继承了A_N1
,现在您将错过控制初始化A_N2
的新实例的顺序(你要初始化实例的顺序是什么?这可能很重要......什么是奇怪的。)
属性和方法混乱:在实例化B_N
的新实例时,您将错过对A.some_property
的访问权限cls
等于B
。但是直接访问B
是可能的,但我的猜测是通过类名访问类中的属性而不是使用A.some_property
)至少很奇怪。
如果不为此创建新的实例或实现特殊逻辑,则无法重新初始化现有实例(感谢 @platinhom 的想法)
classmethods
不能__init__
做什么? __new__
中无法执行任何操作,而__new__
中无法执行操作,因为__init__
执行的操作是可以执行的操作的子集由__init__
执行。
Python Docs, Pickling and unpickling normal class instances#object.getinitargs关于__new__
何时有用的有趣时刻:
当一个pickle类实例被unickled时,通常不会调用它的 init ()方法。
答案 1 :(得分:3)
您在__init__
中可以执行的所有操作也可以在__new__
中完成。
然后,为什么要使用__init__
?
因为您不必将实例存储在变量(示例代码中为obj
)中,后来又需要将其返回。您可以专注于您真正想要做的事情 - 初始化可变对象。
答案 2 :(得分:3)
每When to use __new__ vs. __init__
__new__
是实例创建的第一步。它被称为第一, 并负责返回班级的新实例。在 相反,__init__
没有任何回报;它只负责 在创建实例后初始化实例。
类的类别为type
,type.__call__()
的实现类似于以下内容:
def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls, *args, **kwargs)
if isinstance(obj, cls):
obj.__init__(*args, **kwargs)
return obj
我们知道__init__()
只是__new__()
的一部分可以在返回对象之前对该对象执行任何操作。
一般情况下,除非你继承了str,int,unicode或tuple之类的不可变类型,否则你不应该覆盖
__new__
。
因此,从语言中移除__init__
并不好,最好始终使用__init__()
比使用__new__()
更好。
以下是Low-level constructors and __new__()
的一个历史记录。