Python: аргументированный синглтон

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

Итак, вместо сохранения ссылки на класс/класс в качестве ключа dict, я хочу сохранить переданные аргументы как ключи в dict. Но также могут быть нераскрывающиеся аргументы (например, dict, set).

Каков наилучший способ хранения аргументов класса и сопоставления объектов объектов? Так что я могу вернуть объект, соответствующий аргументам.

Спасибо в любом случае.


EDIT-1: Немного больше объяснений. Пусть говорят, что существует класс следующим образом

class A:
    __metaclass__ == Singleton
    def __init__(arg1, arg2):
        pass

Теперь A(1,2) всегда должен возвращать тот же объект. Но он должен отличаться от A(3,4)

Я думаю, аргументы очень сильно определяют функционирование класса. Скажем, если класс должен делать redis соединения. Возможно, мне захочется создать в качестве параметров 2 объекта с одноточечным доступом с узлами diff redis, но базовый класс/код может быть общим.

Ответ 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, чтобы показать, что в использовании обычного наследования классов нет магии.