gpt4 book ai didi

python - 当类实例由构造函数或 __new__ 创建时,确保 __init__ 只被调用一次

转载 作者:太空狗 更新时间:2023-10-29 20:17:26 25 4
gpt4 key购买 nike

我试图理解当创建过程可以通过构造函数或通过 __new__ 方法时,应该如何创建 Python 类的新实例。特别是,我注意到在使用构造函数时,__init__ 方法将在 __new__ 之后自动调用,而当直接调用 __new__ 时, __init__ 类不会被自动调用。我可以通过在 __new__ 中嵌入对 __init__ 的调用,在显式调用 __new__ 时强制调用 __init__,但是然后 __init__ 将在通过构造函数创建类时最终被调用两次。

例如,考虑下面的玩具类,它存储一个内部属性,即一个名为 datalist 对象:将此视为向量类。

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)

可以使用构造函数(实际上不确定这是否是 Python 中的正确术语)来创建类的新实例,例如

x = MyClass(range(10))

或通过切片,您可以看到在 __getitem__ 方法中调用了对 __new__ 的调用。

x2 = x[0:2]

在第一个实例中,__init__ 将被调用两次(均通过 __new__ 中的显式调用,然后自动再次调用),在第二个实例中调用一次。显然我只希望 __init__ 在任何情况下都被调用一次。有没有在 Python 中执行此操作的标准方法?

请注意,在我的示例中,我可以去掉 __new__ 方法并将 __getitem__ 重新定义为

def __getitem__(self, index):
return MyClass(self.data[index])

但是如果我以后想从 MyClass 继承,这会导致问题,因为如果我像 child_instance[0:2] 那样调用,我会返回MyClass 的实例,而不是子类。

最佳答案

首先,关于 __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__ 返回了 objcls 的一个实例。 (由于 object.__new__ 的第一个参数是 cls,返回的实例是 MyClass 的实例,所以 obj.__init__ 调用 MyClass.__init__,而不是 object.__init__。)


Python 2.2.3 release notes有一个有趣的评论,它阐明了何时使用 __new__ 以及何时使用 __init__:

The __new__ method is called with the class as its first argument; its responsibility is to return a new instance of that class.

Compare this to __init__:__init__ is called with an instance as its first argument, and it doesn't return anything; its responsibility is to initialize the instance.

All this is done so that immutable types can preserve their immutability while allowing subclassing.

The immutable types (int, long, float, complex, str, unicode, and tuple) have a dummy __init__, while the mutable types (dict, list, file, and also super, classmethod, staticmethod, and property) have a dummy __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]

关于python - 当类实例由构造函数或 __new__ 创建时,确保 __init__ 只被调用一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8633959/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com