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)
来修改它们。
比较tuple
和list
的行为:
>>> 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__
初始化)。