Python(和Python C API):__ new__与__init__

Cha*_*l72 118 c python python-c-api

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

在你急于告诉我__new__是用于创建对象并且__init__用于初始化对象之前,让我说清楚:我明白了. 事实上,这种区别对我来说是很自然的,因为我在C++中有经验,我们有新的贴图,它同样将对象分配与初始化分开.

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

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

所以,是的 - 我得到了什么__new__,但尽管如此,我仍然不明白为什么它在Python中有用.给出的示例说,__new__如果要"确保实例变量的初始值" ,这可能很有用.那么,这究竟是什么意思__init__呢?

在C API教程中,显示​​了一个示例,其中创建了一个新类型(称为"Noddy"),并__new__定义了Type的函数.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;
    }

    .....
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果没有__new__此处定义的方法,我们必须使用PyType_GenericNew,它只是将所有实例变量成员初始化为NULL.因此,该__new__方法的唯一好处是实例变量将以空字符串开头,而不是NULL. 但是为什么这个有用,因为如果我们关心确保我们的实例变量被初始化为某个默认值,我们可以在__init__方法中做到这一点?

nco*_*lan 133

差异主要来自可变类型和不可变类型.

__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]
Run Code Online (Sandbox Code Playgroud)

至于为什么它们是分开的(除了简单的历史原因):__new__方法需要一堆样板才能正确(初始对象创建,然后记住最后返回对象).__init__相比之下,方法很简单,因为您只需设置需要设置的属性.

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

  • 创建或以其他方式获取实例(通常使用`super`调用)并返回实例是任何`__new__`实现的必要部分,以及我所指的"样板".相比之下,`pass`是`__init__`的有效实现 - 没有任何必需的行为. (12认同)

sen*_*rle 36

可能还有其他一些用途,__new__但有一个非常明显的用途:你不能在不使用的情况下继承不可变类型__new__.例如,假设你想创建一个元组的子类,它只能包含0和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)
Run Code Online (Sandbox Code Playgroud)

你根本无法做到这一点__init__-如果你试图修改self__init__,解释器会抱怨你试图修改不可变对象.

  • @Alcott,我认为你误解了`__new__`的行为.我们将`cls`显式传递给`__new__`,因为你可以阅读[这里](http://docs.python.org/reference/datamodel.html#object.__new__)`__ new__` _always_需要一个类型作为它的第一个论点.然后它返回该类型的实例.所以我们没有返回超类的实例 - 我们正在返回`cls`的实例.在这种情况下,它就像我们说`tuple .__ new __(ModularTuple,tup)`一样. (2认同)
  • @sakeesh 啊,这是一个非常古老的答案,并且没有考虑到 Python 3 的做事方式。在Python 3中,我认为你是对的,但我必须查看细节才能确定。 (2认同)

Ign*_*ams 32

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

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

  • @martineau:lonetwin 的答案中的`__init__()` 方法在单个对象被实例化时被调用(即当*他们的*`__new__()` 方法返回时),而不是在`Shape.__new__()` 返回时调用。 (2认同)

Nou*_*him 12

不是一个完整的答案,但可能是一个说明差异的东西.

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

  • `__new__`方法的工作是*create*(这意味着内存分配)类的一个实例并返回它.初始化是一个单独的步骤,它通常是用户可见的.如果您遇到特定问题,请另外提问. (2认同)

lon*_*win 5

只想添加一个关于定义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
Run Code Online (Sandbox Code Playgroud)

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

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