什么可以`__init__`做那个`__new__`不能?

时间:2016-02-17 09:09:05

标签: python initialization

在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__会发生什么的问题。什么事情会破裂?有什么事情会变得更难或不可能吗?

3 个答案:

答案 0 :(得分:19)

注意__new__和__init __

之间的区别

在解释缺失的功能之前,让我们回到__new____init__的定义:

__ new __ 是实例创建的第一步。它首先被调用,负责返回您班级的新实例

然而, __ init__不会返回任何内容;它只负责 在创建实例后初始化实例。

将__init__替换为__new __

的后果

主要是你会在灵活性方面松懈。你会得到很多语义上的头痛和初始化和构造的松散分离(通过加入__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__

的后果
  1. B之前初始化A:当您使用__new__方法而不是__init__时,创建B新实例的第一步是调用{{1}作为副作用,您无法在A.__new__初始化之前初始化B(访问并为新B实例分配一些属性)。使用 A 可以提供这种灵活性。

  2. 对初始化顺序失去控制:让我们假设您从两个类(__init__B_N继承了A_N1,现在您将错过控制初始化A_N2的新实例的顺序(你要初始化实例的顺序是什么?这可能很重要......什么是奇怪的。)

  3. 属性和方法混乱:在实例化B_N的新实例时,您将错过对A.some_property的访问权限cls等于B。但是直接访问B是可能的,但我的猜测是通过类名访问类中的属性而不是使用A.some_property)至少很奇怪。

  4. 如果不为此创建新的实例或实现特殊逻辑,则无法重新初始化现有实例(感谢 @platinhom 的想法)

  5. 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__没有任何回报;它只负责   在创建实例后初始化实例。

类的类别为typetype.__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__()的一个历史记录。