在类 X 的 __new__ 方法中取消类型 X 的对象在返回取消取消的对象时调用 __init__ ,为什么?

J. *_*Doe 3 python constructor caching initializer pickle

我有一个对象,它将在第一次使用后被缓存。我将使用 cPickle 模块来完成此操作。如果模块已经缓存,当我尝试下次(在另一个进程中)实例化该对象时,我想使用缓存的对象。以下是我的基本结构:

import cPickle
class Test(object):
    def __new__(cls, name):
        if name == 'john':
            print "using cached object"
            with open("cp.p", "rb") as f:
                obj = cPickle.load(f)
                print "object unpickled"
                return obj
        else:
            print "using new object"
            return super(Test, cls).__new__(cls, name)
    def __init__(self, name):
        print "calling __init__"
        self.name = name
        with open("cp.p", "wb") as f:
            cPickle.dump(self, f)
Run Code Online (Sandbox Code Playgroud)

问题是,当我在__new__方法中取消缓存的对象时,它会调用__init__并重新初始化所有内容。有趣的是,似乎__init__不是在 unpickle 之后调用,而是在返回 unpickle 对象时调用。我添加了一个打印语句来显示这一点(“对象未腌制”)。

我有一个 hacky 解决方法,将以下检查添加到__init__

intiailzed = False
...
...
def __init__(self, name):
    if not self.intialized:
        self.initialized = True
        # Rest of the __init__ here
Run Code Online (Sandbox Code Playgroud)

还有一个类属性叫做initialized,但这显然不太理想。

任何有关如何抑制该__init__方法(或为什么调用它)的见解将不胜感激。

编辑:根据反馈,这是我新提出的解决方案:

class Test(object):
    def __new__(cls, name=None):
        print "calling __new__"
        if name == 'john':
            print "using cached object"
            with open("cp.p", "rb") as f:
                obj = cPickle.load(f)
            print "object unpickled"
            return obj
        else:
            print "using new object"
            obj = super(Test, cls).__new__(cls, name)
            obj.initialize(name)
            return obj

    def __init__(self, name):
        pass

    def initialize(self, name):
        print "calling __init__"
        self.name = name
        with open("cp.p", "wb") as f:
            cPickle.dump(self, f)
Run Code Online (Sandbox Code Playgroud)

use*_*ica 5

obj = Test(name)工作原理如下:

obj = Test.__new__(Test, name)
if isinstance(obj, Test):
    obj.__init__(name)
Run Code Online (Sandbox Code Playgroud)

由于从返回的 unpickled 对象Test.__new__(Test, name)是 的实例Test,因此它的__init__方法被调用。对象是来自还是未腌制的并不重要super(Test, cls).__new__

为了避免此类问题,重写的类__new__通常应从 中返回完全初始化的对象__new__,而不是定义__init__. 他们的子类也应该遵循这个规则。