Хорошо, вот сценарий реального мира: я пишу приложение, и у меня есть класс, который представляет определенный тип файлов (в моем случае это фотографии, но эта деталь не имеет отношения к проблеме). Каждый экземпляр класса Photograph должен быть уникальным для имени файла фотографии.
Проблема заключается в том, что когда пользователь сообщает моему приложению о загрузке файла, мне нужно определить, когда файлы уже загружены, и использовать существующий экземпляр для этого имени файла, а не создавать дубликаты экземпляров в одном и том же имени файла.
Мне кажется, что это хорошая ситуация для использования memoization, и там много примеров этого, но в этом случае я не просто memoizing обычной функции, мне нужно memoising __init__()
. Это создает проблему, потому что к моменту времени __init__()
вызывается уже слишком поздно, поскольку уже создан новый экземпляр.
В моем исследовании я нашел метод Python __new__()
, и я действительно смог написать рабочий тривиальный пример, но он развалился, когда я попытался использовать его на своих объектах реального мира, и я не уверен, почему (единственное, что я могу придумать, это то, что мои объекты реального мира были подклассами других объектов, которые я не могу контролировать, и поэтому были некоторые несовместимости с этим подходом). Это то, что у меня было:
class Flub(object):
instances = {}
def __new__(cls, flubid):
try:
self = Flub.instances[flubid]
except KeyError:
self = Flub.instances[flubid] = super(Flub, cls).__new__(cls)
print 'making a new one!'
self.flubid = flubid
print id(self)
return self
@staticmethod
def destroy_all():
for flub in Flub.instances.values():
print 'killing', flub
a = Flub('foo')
b = Flub('foo')
c = Flub('bar')
print a
print b
print c
print a is b, b is c
Flub.destroy_all()
Какой вывод:
making a new one!
139958663753808
139958663753808
making a new one!
139958663753872
<__main__.Flub object at 0x7f4aaa6fb050>
<__main__.Flub object at 0x7f4aaa6fb050>
<__main__.Flub object at 0x7f4aaa6fb090>
True False
killing <__main__.Flub object at 0x7f4aaa6fb050>
killing <__main__.Flub object at 0x7f4aaa6fb090>
Это прекрасно! Только два экземпляра были сделаны для двух уникальных идентификаторов, и у Flub.instances явно только два указанных.
Но когда я попытался использовать этот подход с объектами, которые я использовал, у меня были всевозможные бессмысленные ошибки о том, как __init__()
принимало только 0 аргументов, а не 2. Поэтому я бы изменил некоторые вещи, и тогда это скажите, что __init__()
нужен аргумент. Совершенно странно.
После некоторого времени сражения с ним я просто сдался и переместил всю черную магию __new__()
в статический метод под названием get
, чтобы я мог вызвать Photograph.get(filename)
, и он будет вызывать только Photograph(filename)
, если имя файла еще не было в Photograph.instances
.
Кто-нибудь знает, где я здесь ошибся? Есть ли лучший способ сделать это?
Еще один способ подумать о том, что он похож на синглтон, за исключением того, что он не глобально singleton, просто singleton-per-filename.
Здесь мой реальный код, использующий staticmethod, получает, если вы хотите увидеть все это вместе.