正如 theheadofabroom 和我在评论中已经提到的那样,依赖不可散列值(例如缓存或记忆)时存在一些可能性。因此,如果您仍然想这样做,下面的示例不会将记忆隐藏在__new__
or __init__
方法。 (自记忆类是危险的,因为记忆标准可能会被您无法控制的代码所欺骗)。
相反,我提供了这个功能memoize
它返回一个类的记忆工厂函数。由于没有通用的方法来区分不可散列的参数,如果它们将产生相当于已经存在的实例的实例,则必须显式提供记忆化语义。这是通过通过keyfunc
功能为memoize
. keyfunc
采用与类相同的参数'__init__
方法并返回一个可散列的键,其相等关系(__eq__
) 决定记忆。
记忆化的正确使用由使用代码负责(提供合理的keyfunc
并使用工厂),因为要记忆的类没有被修改,仍然可以正常实例化。
def memoize(cls, keyfunc):
memoized_instances = {}
def factory(*args, **kwargs):
key = keyfunc(*args, **kwargs)
if key in memoized_instances:
return memoized_instances[key]
instance = cls(*args, **kwargs)
memoized_instances[key] = instance
return instance
return factory
class MemoTest1(object):
def __init__(self, value):
self.value = value
factory1 = memoize(MemoTest1, lambda value : value)
class MemoTest2(MemoTest1):
def __init__(self, value, foo):
MemoTest1.__init__(self, value)
self.foo = foo
factory2 = memoize(MemoTest2, lambda value, foo : (value, frozenset(foo)))
m11 = factory1('test')
m12 = factory1('test')
assert m11 is m12
m21 = factory2('test', [1, 2])
lst = [1, 2]
m22 = factory2('test', lst)
lst.append(3)
m23 = factory2('test', lst)
assert m21 is m22
assert m21 is not m23
我只包括了MemoTest2
作为一个子类MemoTest1
表明使用常规类继承并不涉及任何魔法。