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

时间:2016-04-06 17:28:10

标签: python caching constructor pickle initializer

我有一个在首次使用后会被缓存的对象。我将使用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)

问题在于,当我在__new__方法中取消删除缓存对象时,它会调用__init__并重新初始化所有内容。有趣的是,似乎在解开后不会调用__init__,而是在返回未修改的对象时调用。我添加了一个显示此内容的打印语句("对象未标记")。

我通过在__init__添加以下检查来解决问题:

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

除了一个名为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)

1 个答案:

答案 0 :(得分:3)

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

obj = Test.__new__(Test, name)
if isinstance(obj, Test):
    obj.__init__(name)

由于从Test.__new__(Test, name)返回的unpickled对象是Test的实例,因此调用其__init__方法。对象来自super(Test, cls).__new__还是unpickling无关紧要。

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