144

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

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

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

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

所以,是的 - 我明白__new__,但尽管如此,我仍然不明白为什么它在 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__方法中做到这一点?

4

6 回答 6

150

差异主要出现在可变类型和不可变类型上。

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

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

除了__init__方法更容易编写,以及上面提到的可变与不可变的区别之外,还可以__init__通过在__new__. 不过,这通常是一种可疑的做法——__init__根据需要调用父类方法通常更清楚。

于 2011-02-01T05:16:27.620 回答
45

可能还有其他用途,__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__——如果你试图修改selfin __init__,解释器会抱怨你试图修改一个不可变的对象。

于 2011-02-01T06:00:22.670 回答
42

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

>>> class C(object):
...   def __new__(cls):
...     return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5
于 2011-02-01T05:12:44.317 回答
15

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

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

于 2011-02-28T14:15:48.903 回答
7

只想添加一个关于定义vs的意图(而不是行为)的词。__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

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

如果您正在创建一个常规类(也称为非元类),那么__new__除非它是特殊情况,例如ncoghlan 的答案中的可变与不可变场景(这本质上是定义概念的更具体示例)通过创建的类/类型的初始值/属性,__new__然后通过__init__) 进行初始化。

于 2017-04-12T12:48:14.787 回答
0

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

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

(来源:Python 中的单例模式 - 完整指南 - GeeksforGeeks

于 2021-04-07T12:09:53.447 回答