Хорошо, вот реальный сценарий: я пишу приложение, и у меня есть класс, который представляет определенный тип файлов (в моем случае это фотографии, но эта деталь не имеет отношения к проблеме). Каждый экземпляр класса Photograph должен быть уникальным для имени файла фотографии.
Проблема в том, что когда пользователь говорит моему приложению загрузить файл, мне нужно иметь возможность определить, когда файлы уже загружены, и использовать существующий экземпляр для этого имени файла, а не создавать дубликаты экземпляров с тем же именем файла.
Мне кажется, что это хорошая ситуация для использования мемоизации, и есть много примеров этого, но в этом случае я не просто запоминаю обычную функцию, мне нужно запоминать __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
.
Кто-нибудь знает, где я ошибся? Есть ли лучший способ сделать это?
Другой способ думать об этом состоит в том, что это похоже на синглтон, за исключением того, что это не глобальный синглтон, а просто синглтон на имя файла.
Вот мой реальный код, использующий статический метод get, если хотите увидеть все вместе.