Этот вопрос не предназначен для обсуждения того, является ли шаблон проектирования синглтона желательным, является ли он антипаттерном или для каких-либо религиозных войн, а для обсуждения того, как этот шаблон лучше всего реализовать в Python в такой ситуации. путь, который является наиболее питоническим. В данном случае я определяю "самый питонический", чтобы обозначить, что он следует "принципу наименьшего удивления".
У меня есть несколько классов, которые могли бы стать синглетонами (мой вариант использования - для логгера, но это не важно). Я не хочу загромождать несколько классов добавленной гитарой, когда я могу просто наследовать или украшать.
Лучшие методы:
Способ 1: декоратор
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
@singleton
class MyClass(BaseClass):
pass
Pros
- Декораторы аддитивны таким образом, что зачастую более интуитивно понятны, чем множественное наследование.
Против
- Хотя объекты, созданные с использованием MyClass(), будут настоящими одноэлементными объектами, сам MyClass является функцией, а не классом, поэтому вы не можете вызывать методы класса из него. Также для
m = MyClass(); n = MyClass(); o = type(n)();
, затемm == n && m != o && n != o
Способ 2: базовый класс
class Singleton(object):
_instance = None
def __new__(class_, *args, **kwargs):
if not isinstance(class_._instance, class_):
class_._instance = object.__new__(class_, *args, **kwargs)
return class_._instance
class MyClass(Singleton, BaseClass):
pass
Pros
- Это настоящий класс
Против
- Множественное наследование - тьфу!
__new__
может быть перезаписано во время наследования от второго базового класса? Нужно думать больше, чем нужно.
Метод 3: метакласс
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
#Python2
class MyClass(BaseClass):
__metaclass__ = Singleton
#Python3
class MyClass(BaseClass, metaclass=Singleton):
pass
Pros
- Это настоящий класс
- Автоматически покрывает наследство
- Использует
__metaclass__
для своей цели (и заставил меня осознать это)
Против
- Есть ли?
Метод 4: декоратор возвращает класс с тем же именем
def singleton(class_):
class class_w(class_):
_instance = None
def __new__(class_, *args, **kwargs):
if class_w._instance is None:
class_w._instance = super(class_w,
class_).__new__(class_,
*args,
**kwargs)
class_w._instance._sealed = False
return class_w._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super(class_w, self).__init__(*args, **kwargs)
self._sealed = True
class_w.__name__ = class_.__name__
return class_w
@singleton
class MyClass(BaseClass):
pass
Pros
- Это настоящий класс
- Автоматически покрывает наследство
Против
- Нет ли накладных расходов на создание каждого нового класса? Здесь мы создаем два класса для каждого класса, который мы хотим сделать синглтоном. Хотя в моем случае это нормально, я беспокоюсь, что это может не масштабироваться. Конечно, есть спор о том, должно ли быть слишком легко масштабировать эту модель...
- В чем смысл атрибута
_sealed
- Невозможно вызвать методы с тем же именем в базовых классах, используя
super()
, потому что они будут рекурсивными. Это означает, что вы не можете настроить__new__
и не можете создать подкласс класса, который требует, чтобы вы вызывали до__init__
.
Способ 5: модуль
файл модуля singleton.py
Доводы
- Простое лучше, чем сложное
Cons