Этот шаблон помогает ограничить создание только одного экземпляра класса. Это полезно в тех случаях, когда… вы правильно догадались — требуется только один экземпляр класса, например, один экземпляр класса базы данных, который будет использоваться везде.
Есть много способов создать Singleton в Python:
- Распределитель синглтона
- Одиночный декоратор
- Метакласс синглтона
- моногосударство
Распределитель синглтона
class Singleton: _instance = None def __init__(self): print("I am being called.") def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super(Singleton, cls)\ .__new__(cls, *args, **kwargs) return cls._instance if __name__ == '__main__': s1 = Singleton() s2 = Singleton() print(s1 == s2) # OUTPUT # I am being called. # I am being called. # True
Сначала вызывается метод __new__, и он создает экземпляр класса только в том случае, если он еще не существует.
Но после этого, если есть метод __init__, он вызывается.
Таким образом, при каждом вызове Singleton() также будет вызываться __init__, и, таким образом, мы видим две строки, напечатанные как «Мне звонят».
Эту проблему можно решить другими способами создания синглтона.
Синглтон Декоратор
def singleton(class_): # pass the class for which singleton instance needed. singletons = {} def get_instance(*args, **kwargs): if class_ not in singletons: singletons[class_] = class_(*args, **kwargs) # creates object return singletons[class_] return get_instance @singleton class Singleton: def __init__(self): print('Getting called.') if __name__ == '__main__': s1 = Singleton() s2 = Singleton() print(s1 == s2) # OUTPUT # Getting called. # True
В этом примере мы создали декоратор под названием singleton. Любой класс, который мы хотим сделать одноэлементным, может быть просто аннотирован этим.
Это сэкономило нам усилия при написании одного и того же метода __new__ для каждого создаваемого синглтона, а также решило проблему с распределителем синглтона. Теперь мы видим, что __init__ вызывается только один раз.
Синглтон Метакласс
class SingleType(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(SingleType, cls)\ .__call__(*args, **kwargs) return cls._instances[cls] class Singleton(metaclass=SingleType): def __init__(self): print('Getting called.') if __name__ == '__main__': s1 = Singleton() s2 = Singleton() print(s1 == s2)
моногосударство
class Singleton: __state = { 'name':'Unknown', 'age':21 } def __init__(self): self.__dict__ = self.__state def __str__(self): return f'The person with name {self.name} is {self.age} years old' if __name__ == '__main__': s1 = Singleton() s2 = Singleton() print(s1) print(s2) # OUTPUT # The person with name Unknown is 21 years old # The person with name Unknown is 21 years old
Мы создали состояние класса с некоторыми значениями по умолчанию, и в __init__ мы передаем ссылку на это __state атрибуту __dict__. Это сохранит значения переменных одинаковыми, независимо от того, сколько раз вызывается этот класс. Здесь важно отметить, что s1 и s2 не являются одними и теми же объектами, но мы манипулируем атрибутами, которые они хранят, чтобы они были равны по значению. Я не буду считать это синглтоном, но выбор за вами ;)
Я имею в виду это, вы тоже можете -
https://www.udemy.com/course/design-patterns-python/
Следуйте моему списку шаблонов дизайна Python, чтобы узнать больше — https://medium.com/@rjrichajain00/list/python-design-patterns-4dbd16f87858
Я все еще учусь этому, наверное, как и вы. Давайте обсудим и расширим наше понимание этих удивительных закономерностей. Пожалуйста, оставьте комментарий, если у вас есть вопрос / предложение / исправление. До скорой встречи :)