Python(和Python C API):__new__与__init__

我将要问的问题似乎是Python对__new__和__init__的使用的重复? ,但无论如何,我还不清楚__new____init__之间的实际区别是什么。

在你急于告诉我__new__是用来创build对象的,而__init__是用来初始化对象的之前,让我明白一点: 我明白了。 实际上,这个区别对我来说是很自然的,因为我有C ++的经验,在C ++中我们有了新的位置 ,它将对象分配和初始化分开。

Python C API教程如下解释:

新成员负责创build(而不是初始化)types的对象。 它在Python中作为__new__()方法__new__() 。 … 实现新方法的一个原因是确保实例variables的初始值

所以,是的 – 我得到了什么__new__ ,但是尽pipe如此,我仍然不明白为什么它在Python中很有用。 给出的例子说,如果你想“确保实例variables的初始值”, __new__可能是有用的。 那么,是不是__init__会做什么?

在C API教程中,显示​​了一个示例,其中创build了一个新的Type(称为“Noddy”),并定义了Type的__new__函数。 Noddytypes包含一个名为first的string成员,并且这个string成员被初始化为一个空string,如下所示:

 static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { ..... self->first = PyString_FromString(""); if (self->first == NULL) { Py_DECREF(self); return NULL; } ..... } 

请注意,如果没有这里定义的__new__方法,我们不得不使用PyType_GenericNew ,它将所有的实例variables成员初始化为NULL。 所以__new__方法的唯一好处是实例variables将以空string开始,而不是NULL。 但是,为什么这个有用,因为如果我们关心确保我们的实例variables被初始化为某个默认值,我们可以在__init__方法中做到这一点?

差异主要出现在可变与不可变types之间。

__new__接受一个types作为第一个参数,并且(通常)返回一个新types的实例。 因此它适用于可变和不可变types。

__init__接受一个实例作为第一个参数并修改该实例的属性。 这对不可变的types是不合适的,因为它可以在创build之后通过调用obj.__init__(*args)来修改它们。

比较tuplelist的行为:

 >>> x = (1, 2) >>> x (1, 2) >>> x.__init__([3, 4]) >>> x # tuple.__init__ does nothing (1, 2) >>> y = [1, 2] >>> y [1, 2] >>> y.__init__([3, 4]) >>> y # list.__init__ reinitialises the object [3, 4] 

至于为什么他们是分开的(除了简单的历史原因): __new__方法需要一堆样板才能正确(最初的对象创build,然后记住最后返回对象)。 相反, __init__方法非常简单,因为您只需设置所需的任何属性。

除了__init__方法更容易编写以及上面提到的mutable vs immutable区分之外,还可以利用分离来通过在__new__设置任何绝对必需的实例不variables来调用子类中的父类__init__ 。 这通常是一个令人怀疑的做法 – 根据需要调用父类__init__方法通常更加清晰。

__new__可能还有其他用途,但有一个非常明显的用途:不能使用__new____new__不可变types的子类。 所以举个例子,假设你想创build一个只能包含0和size之间的整数值的元组的子类。

 class ModularTuple(tuple): def __new__(cls, tup, size=100): tup = (int(x) % size for x in tup) return super(ModularTuple, cls).__new__(cls, tup) 

你只是不能用__init__做这个 – 如果你试图修改__init__ self ,解释器会抱怨你试图修改一个不可变的对象。

__new__()可以返回与绑定的类不同的对象。 __init__()只初始化类的现有实例。

 >>> class C(object): ... def __new__(cls): ... return 5 ... >>> c = C() >>> print type(c) <type 'int'> >>> print c 5 

不是一个完整的答案,但也许是说明差异的东西。

当一个对象被创build时, __new__总是被调用。 在某些情况下__init__不会被调用。 一个例子是当你从pickle文件中__new__对象的时候,它们将被分配( __new__ )但是不被初始化( __init__ )。

只是想添加一个关于定义__new____init__意图 (而不是行为)的__new__

当我试图了解定义一个类工厂的最佳方法时,我遇到了这个问题(等等)。 我意识到__new____init__在概念上有所不同的一个方面是, __init__ __new__的好处正是在这个问题中所说的:

所以方法的唯一好处是实例variables将以空string开始,而不是NULL。 但是,为什么这个有用,因为如果我们关心确保我们的实例variables被初始化为某个默认值,那么我们可以在init方法中做到这一点?

考虑到上述情况,当实例实际上是一个类本身时,我们关心实例variables的初始值。 所以,如果我们在运行时dynamic创build一个类对象,并且需要定义/控制正在创build的这个类的后续实例的一些特殊对象,我们将在一个元类的__new__方法中定义这些条件/属性。

直到我真正考虑了这个概念的应用,而不仅仅是这个概念的意义之后,我才感到困惑。 这里有一个例子,希望能够明显地改变这个问题:

 a = Shape(sides=3, base=2, height=12) b = Shape(sides=4, length=2) print(a.area()) print(b.area()) # I want `a` and `b` to be an instances of either of 'Square' or 'Triangle' # depending on number of sides and also the `.area()` method to do the right # thing. How do I do that without creating a Shape class with all the # methods having a bunch of `if`s ? Here is one possibility class Shape: def __new__(cls, sides, *args, **kwargs): if sides == 3: return Triangle(*args, **kwargs) else: return Square(*args, **kwargs) class Triangle: def __init__(self, base, height): self.base = base self.height = height def area(self): return (self.base * self.height) / 2 class Square: def __init__(self, length): self.length = length def area(self): return self.length*self.length 

请注意,这仅仅是一个示范性的例子。 有很多方法可以在不借助上述类工厂方法的情况下获得解决scheme,即使我们select以这种方式实现解决scheme,为了简洁也省略了一些警告(例如,明确声明元类)

如果你正在创build一个常规的类(又称非元类),那么__new__没有什么意义,除非它是ncoghlan的答案中的可变和不可变情况的特殊情况(这实质上是一个更具体的例子定义通过__new__创build的类/types的初始值/属性,然后通过__init__初始化)。