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).
比较的行为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]
Run Code Online (Sandbox Code Playgroud)
至于为什么它们是分开的(除了简单的历史原因):__new__方法需要一堆样板才能正确(初始对象创建,然后记住最后返回对象).__init__相比之下,方法很简单,因为您只需设置需要设置的属性.
除了__init__更容易编写的方法,以及上面提到的可变与不可变的区别之外,还可以利用分离来__init__通过设置任何绝对必需的实例不变量来调用子类中的父类是可选的__new__.这通常是一种可疑的做法 - 通常只需要调用父类__init__方法就更清楚了.
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__,解释器会抱怨你试图修改不可变对象.
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)
Nou*_*him 12
不是一个完整的答案,但可能是一个说明差异的东西.
__new__将永远在必须创建对象时调用.有些情况下__init__不会被调用.一个例子是当你从pickle文件中取消对象时,它们将被分配(__new__)但不会被初始化(__init__).
只想添加一个关于定义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__)进行初始化。