Это немного похоже на ответ от fab, но не совсем то же самое.
одноэлементный контракт не требует, чтобы мы могли вызывать конструктор несколько раз.Поскольку синглтон должен создаваться один раз и только один раз, разве он не должен создаваться только один раз?«Подмена» конструктора, возможно, ухудшает разборчивость.
Поэтому я предлагаю следующее:
class Elvis():
def __init__(self):
if hasattr(self.__class__, 'instance'):
raise Exception()
self.__class__.instance = self
# initialisation code...
@staticmethod
def the():
if hasattr(Elvis, 'instance'):
return Elvis.instance
return Elvis()
Это не исключает использования конструктора или поля instance
в пользовательском коде:
if Elvis() is King.instance:
... если вы точно знаете, что Elvis
еще не создано, а King
имеет.
Но это поощряет пользователи могут использовать метод the
повсеместно:
Elvis.the().leave(Building.the())
Для этого вы также можете переопределить __delattr__()
, чтобы вызвать исключение, если сделана попытка удалить instance
, и переопределить __del__()
так что это вызывает исключение (если мы не знаем, что программа заканчивается ...)
Дальнейшие улучшения
Благодарю тех, кто помог с комментариями и правками, из которых большеДобро пожаловатьХотя я использую Jython, это должно работать более широко и быть поточно-ориентированным.
try:
# This is jython-specific
from synchronize import make_synchronized
except ImportError:
# This should work across different python implementations
def make_synchronized(func):
import threading
func.__lock__ = threading.Lock()
def synced_func(*args, **kws):
with func.__lock__:
return func(*args, **kws)
return synced_func
class Elvis(object): # NB must be subclass of object to use __new__
instance = None
@classmethod
@make_synchronized
def __new__(cls, *args, **kwargs):
if cls.instance is not None:
raise Exception()
cls.instance = object.__new__(cls, *args, **kwargs)
return cls.instance
def __init__(self):
pass
# initialisation code...
@classmethod
@make_synchronized
def the(cls):
if cls.instance is not None:
return cls.instance
return cls()
Примечания:
- Если вы не создаете подкласс из объекта в python2.x вы получите класс в старом стиле, который не использует
__new__
- . При декорировании
__new__
вы должны декорировать с помощью @classmethod, иначе __new__
будет несвязанным методом экземпляра - Возможно, это можно улучшить путем использования метакласса, поскольку это позволит вам сделать
the
свойством уровня класса, возможно, переименовав его в instance