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

时间:2011-02-01 05:07:40

标签: python c python-c-api

我要问的问题似乎与Python's use of __new__ and __init__?重复,但无论如何,我仍然不清楚__new____init__之间的实际区别是什么。

在你急于告诉我__new__用于创建对象而__init__用于初始化对象之前,请允许我说清楚:我明白了。事实上,那个区别对我来说很自然,因为我有C ++的经验,我们有placement new,它同样将对象分配与初始化分开。

Python C API tutorial解释如下:

  

新成员负责   创建(而不是初始化)   该类型的对象。暴露在   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__方法中完成它?

6 个答案:

答案 0 :(得分:124)

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

__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__方法以及上面提到的可变与不可变的区别之外,还可以利用分离来通过设置任何绝对来调用子类中的父类__init__ __new__中必需的实例不变量。这通常是一种可疑的做法 - 在必要时调用父类__init__方法通常更为清晰。

答案 1 :(得分:32)

__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__执行此操作 - 如果您尝试修改self中的__init__,则解释程序会抱怨您正在尝试修改不可变对象。

答案 2 :(得分:29)

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

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

答案 3 :(得分:11)

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

必须在创建对象时始终调用

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

答案 4 :(得分:2)

只想添加关于定义__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's answer中的可变与不可变场景(这实际上是定义通过__new__创建的类/类型的初始值/属性的概念的一个更具体的例子,然后通过__init__进行初始化。)

答案 5 :(得分:0)

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

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

(来源:Singleton Pattern in Python - A Complete Guide - GeeksforGeeks