我正在尝试理解如何在创建过程可以通过构造函数或通过__new__
方法创建Python类的新实例。特别是,我注意到在使用构造函数时,__init__
方法将在__new__
之后自动调用,而在直接调用__new__
时,__init__
类将不会被自动调用。我可以通过在__init__
内嵌入对__new__
的调用来明确调用__init__
时强制调用__new__
,但__init__
最终会被调用两次当通过构造函数创建类时。
例如,考虑以下玩具类,它存储一个内部属性,即名为list
的{{1}}对象:将此视为向量类的开头很有用。
data
可以使用构造函数创建类的新实例(实际上不确定这是否是Python中的正确术语),例如
class MyClass(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
obj.__init__(*args, **kwargs)
return obj
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.__new__(type(self), self.data[index])
def __repr__(self):
return repr(self.data)
或通过切片,您可以在x = MyClass(range(10))
方法中调用__new__
来调用。
__getitem__
在第一个实例中,x2 = x[0:2]
将被调用两次(通过__init__
中的显式调用然后再自动调用),并且在第二个实例中调用一次。显然,我只想在任何情况下调用__new__
一次。有没有一种标准的方法在Python中执行此操作?
请注意,在我的示例中,我可以摆脱__init__
方法并将__new__
重新定义为
__getitem__
但如果我以后想要继承def __getitem__(self, index):
return MyClass(self.data[index])
,这会导致问题,因为如果我拨打MyClass
这样的电话,我会找回child_instance[0:2]
的实例,而不是儿童班。
答案 0 :(得分:10)
首先,关于__new__
和__init__
的一些基本事实:
__new__
是构造函数。__new__
通常返回cls
的第一个参数的实例。 __new__
返回cls
,__new__
causes Python to call __init__
。__init__
是初始值设定项。它修改了实例(self
)
由__new__
返回。它不需要返回self
。 MyClass
定义:
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
obj.__init__(*args, **kwargs)
return obj
MyClass.__init__
被调用两次。在明确调用obj.__init__
之后,第二次因为__new__
返回obj
而调用cls
的实例。 (由于object.__new__
的第一个参数是cls
,因此返回的实例是MyClass
的实例,因此obj.__init__
调用MyClass.__init__
,而不是object.__init__
。 )
Python 2.2.3 release notes有一个有趣的评论,可以说明何时使用__new__
以及何时使用__init__
:
调用
__new__
方法,将类作为其第一个参数;它的 责任是返回该类的新实例。将此与
__init__
进行比较:调用__init__
作为其实例 第一个参数,它不返回任何东西;它的责任是 初始化实例。所有这一切都是为了使不可变类型可以保留它们 允许子类化的不变性。
不可变类型(int,long,float,complex,str,unicode和 元组)有一个虚拟
__init__
,而可变类型(字典,列表, 文件,还有super,classmethod,staticmethod和property)都有 假__new__
。
因此,使用__new__
来定义不可变类型,并使用__init__
来定义可变类型。虽然可以定义两者,但您不需要这样做。
因此,由于MyClass是可变的,因此您应该只定义__init__
:
class MyClass(object):
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return type(self)(self.data[index])
def __repr__(self):
return repr(self.data)
x = MyClass(range(10))
x2 = x[0:2]
答案 1 :(得分:1)
有几件事情不应该做:
__init__
__new__
__new__
正如您已经看到的,在创建给定类的对象时,会自动调用__new__
和__init__
方法。直接使用它们会破坏此功能(但允许在另一个__init__
内调用__init__
,如下例所示。
您可以在获取__class__
属性的任何方法中获取对象的类,如以下示例所示:
class MyClass(object):
def __new__(cls, *args, **kwargs):
# Customized __new__ implementation here
return obj
def __init__(self, data):
super(MyClass, self).__init__(self)
self.data = data
def __getitem__(self, index):
cls = self.__class__
return cls(self.data[index])
def __repr__(self):
return repr(self.data)
x = MyClass(range(10))
x2 = x[0:2]
答案 2 :(得分:1)
使用MyClass(args)
创建类的实例时,默认实例创建顺序如下:
MyClass.__new__(args)
被调用以获取新的“空白”实例new_instance.__init__(args)
(new_instance
是从上面调用__new__
返回的实例),以初始化新实例的属性[1] new_instance
作为MyClass(args)
从此可以看出,自己调用MyClass.__new__
将而不是会导致调用__init__
,因此您最终会得到一个未初始化的实例。同样明确的是,将__init__
拨打__new__
也不正确,因为MyClass(args)
会__init__
两次致电。
问题的根源是:
我正在尝试理解Python类的新实例应该如何 当创建过程可以通过构造函数或者创建时创建 通过新方法
创建过程通常不应该通过__new__
方法。 __new__
是普通实例创建协议的部分,因此您不应期望它为您调用整个协议。
一个(坏的)解决方案是亲自手动实施此协议;而不是:
def __getitem__(self, index):
return self.__new__(type(self), self.data[index])
你可以:
def __getitem__(self, index):
new_item = self.__new__(type(self), self.data[index])
new_item.__init__(self.data[index])
return new_item
但实际上,你想要做的事情并不是完全混淆__new__
。默认的__new__
适用于您的情况,默认的实例创建协议适用于您的情况,因此您既不应该实现__new__
也不应该直接调用它。
你想要的是通过调用类来以正常方式创建类的新实例。如果没有继承,并且您认为没有继承,只需将self.__new__(type(self), self.data[index])
替换为MyClass(self.data[index])
。
如果您认为有一天可能是MyClass
的子类想要通过切片而不是MyClass
来创建子类的实例,那么您需要动态获取{{1}的类并调用它。你已经知道如何做到这一点,因为你在程序中使用它! self
将返回type(self)
的类型(类),然后您可以通过self
MyClass
直接调用它来完全调用。
顺便说一句,type(self)(self.data[index])
的意思是你想要在初始化之前自定义获取类的“新”空白实例的过程。几乎所有时间,这都是完全没必要的,默认__new__
也没问题。
在两种情况下,您只需要__new__
:
__new__
中创建的,而不是之后的。{/ li>
醇>
作为point(1)的概括,你可以让__new__
返回你喜欢的任何东西(不一定是类的实例),以便以某种任意奇怪的方式调用一个类。不过,这似乎总是让人感到困惑而不是有用。
[1]我相信事实上协议稍微复杂一些; __init__
仅在__new__
返回的值上调用,如果它是为启动进程而调用的类的实例。然而,事实并非如此。