Вопрос по python – Как мне запомнить экземпляр класса в Python?
===============================================================

Хорошо, вот сценарий реального мира: я пишу приложение, и у меня есть класс, который представляет определенный тип файлов (в моем случае это фотографии, но эта деталь не имеет отношения к проблеме). Каждый экземпляр класса Photograph должен быть уникальным для имени файла фотографии.

Проблема в том, что когда пользователь говорит моему приложению загрузить файл, я должен иметь возможность определить, когда файлы уже загружены, и использовать существующий экземпляр для этого имени файла, а не создавать дубликаты экземпляров с тем же именем файла.

Мне кажется, что это хорошая ситуация, чтобы использовать запоминание, и существует множество примеров этого, но в этом случае я не просто запоминаю обычную функцию, мне нужно запоминать`__init__()`, Это создает проблему, потому что к тому времени`__init__()` вызывается, это уже слишком поздно, поскольку уже создан новый экземпляр.

В моем исследовании я нашел Python`__new__()` метод, и я действительно смог написать рабочий тривиальный пример, но он развалился, когда я попытался использовать его на своих объектах реального мира, и я не уверен почему (единственное, что я могу думать, это то, что мой реальный объекты мира были подклассами других объектов, которые я не могу реально контролировать, и поэтому при таком подходе были некоторые несовместимости). Вот что у меня было:

Какой вывод это:

Это прекрасно! Только два экземпляра были сделаны для двух уникальных идентификаторов, а в Flub.instances явно есть только два перечисленных.

Но когда я попытался использовать этот подход с объектами, которые я использовал, я получил всевозможные бессмысленные ошибки о том, как`__init__()` взял только 0 аргументов, а не 2. Так что я бы изменил некоторые вещи, и тогда он сказал бы мне, что`__init__()` нужен аргумент. Совершенно странно.

После некоторого времени борьбы с ним, я просто сдался и переместил все`__new__()` черная магия в статический метод называется`get`такой, чтобы я мог позвонить`Photograph.get(filename)` и это будет только позвонить`Photograph(filename)` если имя файла не было уже в`Photograph.instances`.

Кто-нибудь знает, где я ошибся здесь? Есть ли лучший способ сделать это?

Другой способ думать об этом состоит в том, что он аналогичен синглтону, за исключением того, что он не глобально синглтон, а только синглтон на имя файла.

[Вот мой реальный код с использованием staticmethod get](https://github.com/robru/gottengeography/blob/908a1842116dc26ca57e3e4db6802fa563bc2008/gg/photos.py#L74) если вы хотите увидеть все это вместе.

Using memoize
Вы можете использовать памятку, но вы должны украситьclass, не`__init__` метод. Предположим, у нас есть этот мемоизатор:

Теперь вам просто нужно украсить класс:

Давайте посмотрим тест?

Выход ниже. Обратите внимание, что одинаковые параметры дают одинаковый идентификатор возвращаемого объекта:

В любом случае, я бы предпочел создать функцию для генерации объектов и их запоминания. Кажется, чище для меня, но это может быть какая-то неуместная любимая мозоль

Using `__new__`:
Или, конечно, вы можете переопределить`__new__`, Несколько дней назад я отправил[ответ о плюсах, минусах и лучших практиках переопределения`__new__`](https://stackoverflow.com/a/10789315/287976) это может быть полезно. В основном, это говорит, чтобы всегда проходить`*args, **kwargs` на ваш`__new__` метод.

Я, например, предпочел бы запомнить функцию, которая создает объекты, или даже написать конкретную функцию, которая позаботилась бы о том, чтобы объект никогда не воссоздавался с одним и тем же параметром. Конечно, однако, это в основном мое мнение, а не правило.



Параметры для`__new__` также передается`__init__`, так:

Вы должны принять`flubid` аргумент там, даже если вы не используете его в`__init__`

Вот соответствующий комментарий, взятый из[typeobject.c в Python2.7.3](https://hg.python.org/cpython/file/70274d53c1dd/Objects/typeobject.c#l2795)



И тогда вы украшаетеclass с этим не`__init__`, Хотя Brandizzi предоставил мне эту ключевую информацию, его примерный декоратор не работал должным образом.

Я нашел эту концепцию довольно тонкой, но в основном, когда вы используете декораторы в Python, вы должны понимать, что то, что декорируется (будь то метод или класс), на самом делеreplaced by сам декоратор. Например, когда я пытаюсь получить доступ`Photograph.instances` или же`Camera.generate_id()` (статический метод), я не могу получить к ним доступ, потому что`Photograph` фактически не относится к исходному классу Photograph, оно относится к`memoized` function (из примера Брэндиззи).

Чтобы обойти это, мне нужно было создать класс декоратора, который на самом деле брал все атрибуты и статические методы из декорированного класса и выставлял их как свои собственные. Почти как подкласс, за исключением того, что класс декоратора не знает заранее, какие классы он будет декорировать, поэтому он должен скопировать атрибуты по факту.

Конечным результатом является то, что любой экземпляр`memoize` class становится почти прозрачной оболочкой вокруг фактического класса, который он украсил, за исключением того, что попытка создать его экземпляр (но на самом деле вызывать его) предоставит вам кэшированные копии, когда они станут доступны.