我要问的问题似乎与Python's use of __new__ and __init__?重复,但无论如何,我仍然不清楚__new__
和__init__
之间的实际区别是什么。
在你急于告诉我__new__
用于创建对象而__init__
用于初始化对象之前,请允许我说清楚:我明白了。事实上,那个区别对我来说很自然,因为我有C ++的经验,我们有placement new,它同样将对象分配与初始化分开。
新成员负责 创建(而不是初始化) 该类型的对象。暴露在 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__
方法中完成它?
答案 0 :(得分:124)
差异主要来自可变类型和不可变类型。
__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 :(得分: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)