假设我有一个叫做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
答案 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)
您可能希望使用weakref的WeakValueDictionary作为符号注册表,以便垃圾收集可以在不再引用符号时回收内存。
您可以使用以下类来定义实习对象的内容。您的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