Ответ 1
Как уже упоминалось в комментариях к предыдущей заметке, есть некоторые шансы, когда полагаются на не-хешируемые значения, например, кэширование или memoization. Поэтому, если вы все еще хотите сделать именно это, следующий пример не скрывает memoization в методе __new__
или __init__
. (Класс самопоминания будет опасным, потому что критерий memoization может быть обманут кодом, который вы не контролируете).
Вместо этого я предоставляю функцию memoize
, которая возвращает memoizing функцию factory для класса. Поскольку нет общего способа рассказать из аргументов, не имеющих хэширования, если они приведут к экземпляру, который эквивалентен уже существующему isntance, семантика memoization должна быть предоставлена явно. Это достигается передачей функции keyfunc
в memoize
. keyfunc
принимает те же аргументы, что и метод класса __init__
, и возвращает хешируемый ключ, отношение равенства которого (__eq__
) определяет memoization.
Правильное использование memoization несет ответственность за использование кода (предоставление разумного keyfunc
и использование factory), так как класс, который должен быть сохранен в памяти, не изменяется и может все еще быть создан как обычно.
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
, чтобы показать, что в использовании обычного наследования классов нет магии.