Как можно использовать декораторы для реализации паттерна Singleton?

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

Пример:
  
  def singleton(cls):
      instance = None
      def wrapper(*args, **kwargs):
          nonlocal instance
          if instance is None:
              instance = cls(*args, **kwargs)
          return instance
      return wrapper

  @singleton
  class MyClass:
      pass
  
  

Реализовать паттерн Singleton с помощью декоратора в Python можно следующим образом:


def singleton(cls):
    """Декоратор для реализации паттерна Singleton."""
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance


@singleton
class MySingleton:
    def __init__(self, value):
        self.value = value

    def get_value(self):
        return self.value

# Пример использования
singleton1 = MySingleton(10)
singleton2 = MySingleton(20)

print(singleton1.get_value()) # Выведет 10
print(singleton2.get_value()) # Выведет 10 (тот же самый экземпляр)

print(singleton1 is singleton2) # Выведет True
  

Разберем код:

  • Функция `singleton(cls)` принимает класс (`cls`) в качестве аргумента.
  • Внутри `singleton` создается словарь `instances`, который будет хранить экземпляры классов. Ключом словаря является класс, а значением - его экземпляр.
  • Определяется внутренняя функция `get_instance(*args, **kwargs)`, которая и будет возвращена в качестве замененного класса.
  • `get_instance` проверяет, существует ли уже экземпляр класса `cls` в словаре `instances`. Если нет, то экземпляр создается и сохраняется в словаре.
  • `get_instance` всегда возвращает один и тот же экземпляр класса, хранящийся в словаре `instances`.
  • Декоратор `@singleton` применяется к классу `MySingleton`. Это эквивалентно записи `MySingleton = singleton(MySingleton)`. В результате, при попытке создать экземпляр `MySingleton`, фактически вызывается функция `get_instance`.

Преимущества использования декоратора для Singleton:

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

Важно отметить:

  • Этот подход не предотвращает создание новых экземпляров класса через манипуляции с `__new__` или десериализацию. Более строгие реализации могут быть необходимы в определенных случаях.
0