Этот шаблон помогает ограничить создание только одного экземпляра класса. Это полезно в тех случаях, когда… вы правильно догадались — требуется только один экземпляр класса, например, один экземпляр класса базы данных, который будет использоваться везде.
Есть много способов создать 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
Я все еще учусь этому, наверное, как и вы. Давайте обсудим и расширим наше понимание этих удивительных закономерностей. Пожалуйста, оставьте комментарий, если у вас есть вопрос / предложение / исправление. До скорой встречи :)