Python (和 Python C API) :__ new__ vs__ init__

我要问的问题似乎是 Python 使用 _ _ new _ _ 和 _ _ init _ _?的复制品,但无论如何,我仍然不清楚 __new____init__之间的实际差异究竟是什么。

在你急着告诉我 __new__是用来创建对象的,而 __init__是用来初始化对象的之前,让我先说清楚: 我明白。事实上,这种区别对我来说是很自然的,因为我在 C + + 中有经验,我们有 新的位置,它类似地将对象分配和初始化分离开来。

Python C API 教程是这样解释的:

新成员负责 创建(相对于初始化) 类型的对象。它在 Python 作为 __new__()方法..。 实现新方法的一个原因是确保 实例变量 .

所以,是的-我 走开什么 __new__做,但尽管如此,我 还是不明白为什么它在 Python 中有用。给出的例子说明,如果您想“确保实例变量的初始值”,__new__可能是有用的。这不正是 __init__要做的吗?

在 C API 教程中,一个示例显示了创建新 Type (称为“ Noddy”)的地方,并定义了 Type 的 __new__函数。Noddy 类型包含一个名为 first的字符串成员,该字符串成员被初始化为一个空字符串,如下所示:

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,它只是将所有的实例变量成员初始化为 NULL。因此,__new__方法的唯一好处是,实例变量开始时是一个空字符串,而不是 NULL。但是为什么这样做是有用的,因为如果我们关心确保我们的实例变量被初始化为某个默认值,我们可以只在 __init__方法中这样做?

37021 次浏览

__new__()可以返回其绑定到的类以外的类型的对象。__init__()只初始化类的现有实例。

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

区别主要出现在可变类型和不可变类型之间。

__new__接受 类型作为第一个参数,并且(通常)返回该类型的新实例。因此,它适合与可变类型和不可变类型一起使用。

__init__接受 例子作为第一个参数并修改该实例的属性。这对于不可变类型是不合适的,因为它允许在创建后通过调用 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__方法需要一大堆样板才能正确(初始对象创建,然后记住在最后返回对象)。相比之下,__init__方法非常简单,因为只需要设置需要设置的任何属性。

除了 __init__方法更容易编写,以及上面提到的可变与不可变的区别之外,还可以利用这种分离,通过在 __new__中设置任何绝对需要的实例不变量,使子类中的父类 __init__的调用成为可选的。这通常是一个可疑的做法-通常更清楚的做法是根据需要调用父类 __init__方法。

__new__可能还有其他用途,但有一个非常明显的用途: 如果不使用 __new__,就不能对不可变类型进行子类化。例如,假设您想创建一个 tuple 的子类,它只能包含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__不会被调用。一个例子是,当您从 pickle 文件中取消 pickle 对象时,它们将被分配(__new__) ,但不会被初始化(__init__)。

Just want to add a word about the 意图 (as opposed to the behavior) of defining __new__ versus __init__.

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

因此,_ _ new _ _ 方法的唯一好处是,实例变量开始时是一个空字符串,而不是 NULL。但是,既然我们关心的是确保实例变量被初始化为某个默认值,那么为什么这个方法总是有用呢? 我们可以在 _ _ init _ _ 方法中这样做?

考虑到上述场景,当 例子实际上是一个类本身时,我们关心的是实例变量的初始值。因此,如果我们在运行时动态地创建一个类对象,并且我们需要定义/控制这个类的后续实例,我们将在元类的 __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

注意,这只是一个示范性的例子。有多种方法可以得到一个解决方案,而不需要采用类工厂的方法,就像上面那样,即使我们选择以这种方式实现解决方案,为了简洁起见,也有一些小的警告(例如,显式地声明元类)

如果你正在创建一个常规类(也就是一个非元类) ,那么 __new__实际上是没有意义的,除非它是像 Ncoghlan 的回答答案中的可变与不可变场景那样的特殊情况(这本质上是一个更具体的例子,定义了通过 __new__创建的类/类的初始值/属性,然后通过 __init__进行初始化)。

__new__的一个特殊用途是使类成为一个单例:

class SingletonClass(object):
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(SingletonClass, cls).__new__(cls)
return cls.instance

(资料来源: Python 单例模式-完整指南-GeeksforGeeks)