2011-02-01 61 views
93

我现在要问的问题似乎是Python's use of __new__ and __init__?的重复,但无论如何,我现在还不清楚__new____init__之间的实际区别是什么。Python(和Python C API):__new__与__init__

在您急于告诉我__new__用于创建对象并且__init__用于初始化对象之前,请让我清楚:我明白了。事实上,这种区分对我来说很自然,因为我有C++的经验,我们有placement new,它类似地将对象分配与初始化分开。

Python C API tutorial解释这样的:

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

所以,是的 - 我得到做什么__new__,但尽管这样,我还是不明白为什么它在Python是非常有用的。给出的例子说如果你想“确保实例变量的初始值”,__new__可能是有用的。那么,是不是__init__会做什么?

在C API教程中,显示​​了一个示例,其中创建了一个新类型(称为“Noddy”),并定义了类型的__new__函数。诺迪类型包含称为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__方法中做到这一点?

回答

105

不同之处主要在于可变与不可变类型。

__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__方法是更容易编写和可变VS不变区别

除了上面提到的,分离也可以利用来使调用子类在__new__设置任何绝对必要的情况下不变量可选的父类__init__。但这通常是一种可疑的做法 - 根据需要调用父类__init__方法通常会更清楚。

+1

您在`__new__`中引用为“样板”的代码不是样板,因为样板不会更改。有时你需要用不同的东西替换特定的代码。 – 2013-01-23 07:34:09

+10

创建或以其他方式获取实例(通常使用`super`调用)并返回实例是任何`__new__`实现的必要部分,以及我所指的“样板”。相比之下,`pass`是`__init__`的有效实现 - 不需要任何行为。 – ncoghlan 2013-01-24 10:08:28

24

__new__()可以返回与其绑定类别不同的​​对象。 __init__()只初始化该类的现有实例。

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

这是迄今为止最精简的解释。 – Tarik 2011-11-12 18:05:27

+0

不完全正确。我有`__init__`方法,其中包含类似`self .__ class__ = type(...)`的代码。这会导致该对象与您以前创建的对象不同。我不能像你所做的那样把它变成一个`int` ......我得到关于堆类型的错误......但是我将它分配给一个动态创建的类的例子起作用。 – ArtOfWarfare 2016-01-21 18:52:21

31

__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__,解释器会抱怨你试图修改不可变对象。

11

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

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

0

只是想添加一个关于定义__new____init__意图(与行为相反)的字。

当我试图理解定义类工厂的最佳方法时,我遇到了这个问题(除其他外)。我意识到,在__new____init__概念上不同的方法之一是事实,__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 

注意这只是一个demonstartive例子。有很多方法可以在不采用类似上述的类工厂方法的情况下获得解决方案,即使我们选择以这种方式实现解决方案,为了简洁也省略了一些警告(例如,明确声明元类)

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