问题:Python(和Python C API):__new__与__init__
我要问的问题似乎是Python对__new__和__init__的重复使用?,但无论如何,我仍然不清楚__new__
和之间的实际区别是什么__init__
。
在您急于告诉我__new__
创建对象和__init__
初始化对象之前,请让我明确:我明白了。 实际上,这种区分对我来说是很自然的,因为我在C ++中有经验,在那里我们放置了new,它类似地将对象分配与初始化分开。
在Python的C API教程解释它是这样的:
新成员负责创建(而不是初始化)该类型的对象。它在Python中作为
__new__()
方法公开。… 实施新方法的原因之一是要确保实例变量的初始值。
所以,是的-我明白了__new__
,但是尽管如此,我仍然不明白为什么它在Python中很有用。给出的示例说,__new__
如果要“确保实例变量的初始值” ,这可能会很有用。好吧,这不正是要做__init__
什么吗?
在C API教程中,显示了一个示例,其中创建了新的Type(称为“ 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;
}
.....
}
请注意,如果没有在此__new__
定义的方法,我们将不得不使用PyType_GenericNew
,它只会将所有实例变量成员初始化为NULL。因此,该__new__
方法的唯一好处是实例变量将从一个空字符串开始,而不是NULL。 但是,为什么这会有用呢,因为如果我们要确保将实例变量初始化为某个默认值,那么我们可以在__init__
方法中做到这一点?
回答 0
差异主要发生在可变与不可变类型之间。
__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__
方法会更清晰。
回答 1
可能还有其他用途,__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
__new__()
可以返回与其绑定的类不同类型的对象。__init__()
仅初始化该类的现有实例。
>>> class C(object):
... def __new__(cls):
... return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5
回答 3
这不是一个完整的答案,但也许可以说明差异。
__new__
当必须创建一个对象时,它将总是被调用。在某些情况下__init__
不会被呼叫。一个示例是,当您从pickle文件中解开对象时,它们将被分配(__new__
)但未初始化(__init__
)。
回答 4
只是想添加一个关于定义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__
。