Как я могу запомнить экземпляр класса в Python?

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

21
задан robru 6 June 2012 в 07:43
поделиться