腌制“实习”对象

时间:2012-03-28 14:16:39

标签: python pickle

假设我有一个叫做Symbol的课程。在任何给定的时间,我想要一个具有给定id的符号的一个且只有一个副本。例如

registry = {}

class Symbol(object):
    def __init__(self, id):
       self.id = id
    def __eq__(self, other):
       return self is other

def symbol(id):
    if id not in registry:
        registry[id] = Symbol(id)

    return registry[id]

我希望能够挑选我的Symbol对象,但我无法想象如何让cPickle调用我的符号工厂。现在我可以实现getstate / setstate覆盖,但是仍然不会将未修改的对象与已存在于注册表中的对象进行合并。如何在保留符号与ID的1:1比例的同时腌制上述类?


编辑(更新标题为州“interned”而不是“singleton”):

让我解释一下用例。我们将这些符号用作dicts中的键。让他们实习可以大大提高绩效

我需要做什么:

x = symbol("x")

y = pickle.loads(pickle.dumps(x))

x is y == True

3 个答案:

答案 0 :(得分:3)

由于您不希望多个具有给定ID的对象,请提供自定义__new__方法来代替您的symbol函数。

class Symbol(object):
    registry = {}
    def __new__(cls, *args, **kwargs):
        id_ = args[0]
        return Symbol.registry.setdefault(_id, object.__new__(cls, *args, **kwargs))

    def __init__(self, id):
       self.id = id

现在您不需要工厂函数来创建Symbol个对象。

$ a = Symbol('=')
$ b = Symbol('=')
$ a is b
True

答案 1 :(得分:0)

您可以尝试使用pickle.Unpickler子类,并在load方法中实现加载逻辑。

但是你需要某种键才能知道对象在运行时是否已经存在(返回引用而不是新实例)。这将引导您重新实现python对象空间。

我建议尝试找到更适合您实际问题的其他数据结构。

答案 2 :(得分:0)

您可能希望使用weakrefWeakValueDictionary作为符号注册表,以便垃圾收集可以在不再引用符号时回收内存。

您可以使用以下类来定义实习对象的内容。您的Symbol类(或任何其他类)可以继承它。

class Interned (object):
    # you need to create this registry in each class if the keys are not unique system-wide
    registry = weakref.WeakValueDictionary() 
    def __new__(cls, *args, **kwargs):
        assert 0 < len(args)
        if not args[0] in cls.registry: # don't use setdefault to avoid creating unnecessary objects
            o = object.__new__(cls, *args, **kwargs) # o is a ref needed to avoid garbage collection within this call
            cls.registry[args[0]] = o
            return o
        return cls.registry[args[0]]
    def __eq__(self, other):
        return self is other
    def __hash__(self): 
        # needed by python 3
        return id(self)
    def __ne__(self, other):
        return not self is other

您的代码变为:

class Symbol(Interned):
    def __init__(self, id):
       self.id = id

导致:

$ a = Symbol('=')
$ b = Symbol('=')
$ a is b
True