热门标签:
Q:

Python(和Python C API):__new__versus__init__

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

在你急于告诉我__new__用于创建对象,__init__用于初始化对象之前,让我明确一点:我明白了。事实上,这种区别对我来说是很自然的,因为我有C++的经验,我们有放置new,它类似地将对象分配与初始化分开。

Python C API教程这样解释:

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

所以,是的-Igetwhat__new__does,但是尽管如此,我仍然不明白为什么它在Python中有用。 给出的例子说,如果你想"保证实例变量的初始值",__new__可能是有用的。 那么,这不正是__init__将要做的吗?

在C API教程中,显示了一个示例,其中创建了一个新类型(称为"Noddy"),并定义了该类型的__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__方法中这样做?

原网址
A:

区别主要在于可变类型和不可变类型。

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

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

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

所有回答

共 5 条

author avatar

__new__可能还有其他用途,但有一个非常明显的用途:你不能在不使用__new__的情况下对不可变类型进行子类化。 例如,假设你想创建一个元组的子类,它只能包含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__中的__init__,解释器会抱怨你试图修改一个不可变对象。

author avatar

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

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

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

__new__总是在必须创建对象时被调用。 有些情况下__init__不会被调用。 一个例子是,当您从pickle文件中解压缩对象时,它们将被分配(__new__),但未初始化(__init__)。

author avatar

只想添加一个关于定义__new____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

请注意,这只是一个示范性的例子。 有多种方法可以在不诉诸类工厂方法的情况下获得解决方案,即使我们选择以这种方式隐含解决方案,为了简洁起见,也有一些注意事项(例如,显式声明元类)

如果你正在创建一个常规类(a.k.a非元类),那么__new__没有真正意义,除非它是特殊情况,就像ncoghlan的答案answer中的可变与不可变场景(这本质上是定义通过__new__创建的类/类型的初始值/属性的概念的一个更具体的例子,然后通过__init__初始化)...

author avatar

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

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

(source:Singleton Pattern in Python-A Complete Guide-GeeksforGeeks)

相似问题