Source code for hbutils.design.singleton
"""
Overview:
Implement of singleton design pattern.
"""
__all__ = [
'SingletonMeta',
'ValueBasedSingletonMeta',
'SingletonMark',
]
[docs]class SingletonMeta(type):
"""
Overview:
Meta class for singleton mode.
Example:
>>> class MyService(metaclass=SingletonMeta):
>>> def get_value(self):
>>> return 233
>>>
>>> s = MyService()
>>> s.get_value() # 233
>>> s1 = MyService()
>>> s1 is s # True
.. note::
In native singleton pattern, the constructor is not needed because \
only one instance will be created in the whole lifetime. So when \
:class:`SingletonMeta` is used as metaclass, please keep the constructor \
be non-argument, or just ignore the ``__init__`` function.
"""
_instances = {}
[docs] def __call__(cls):
if cls not in cls._instances:
cls._instances[cls] = super(SingletonMeta, cls).__call__()
return cls._instances[cls]
[docs]class ValueBasedSingletonMeta(type):
"""
Overview:
Meta class for value based singleton mode.
Example:
>>> class MyData(metaclass=ValueBasedSingletonMeta):
>>> def __init__(self, value):
>>> self.__value = value
>>>
>>> @property
>>> def value(self):
>>> return self.__value
>>>
>>> d1 = MyData(1)
>>> d1.value # 1
>>> d2 = MyData(1)
>>> d3 = MyData(2)
>>> d2 is d1 # True
>>> d2 is d3 # False
.. note::
This is an external case of singleton pattern. It can only contain one argument \
(must be positional-supported), which differs from the native singleton case.
"""
_instances = {}
[docs] def __call__(cls, value):
key = (cls, value)
if key not in cls._instances:
cls._instances[key] = super(ValueBasedSingletonMeta, cls).__call__(value)
return cls._instances[key]
[docs]class SingletonMark(metaclass=ValueBasedSingletonMeta):
"""
Overview:
Singleton mark for some situation.
Can be used when some default value is needed, especially when `None` has meaning which is not default.
Example:
>>> NO_VALUE = SingletonMark("no_value")
>>> NO_VALUE is SingletonMark("no_value") # True
.. note::
:class:`SingletonMark` is a value-based singleton class, can be used to create an unique \
value, especially in the cases which ``None`` is not suitable for the default value.
"""
[docs] def __init__(self, mark):
"""
Overview:
Constructor of :class:`SingletonMark`, can create a singleton mark object.
"""
self.__mark = mark
@property
def mark(self):
"""
Overview:
Get mark string of this mark object.
Returns:
- mark (:obj:`str`): Mark string
"""
return self.__mark
[docs] def __hash__(self):
"""
Overview:
:class:`SingletonMark` objects are hash supported. can be directly used \
in :class:`dict` and :class:`set`.
"""
return hash(self.__mark)
[docs] def __eq__(self, other):
"""
Overview:
:class:`SingletonMark` objects can be directly compared with ``==``.
Examples::
>>> mark1 = SingletonMark('mark1')
>>> mark1x = SingletonMark('mark1')
>>> mark2 = SingletonMark('mark2')
>>> mark1 == mark1
True
>>> mark1 == mark1x
True
>>> mark1 == mark2
False
"""
if other is self:
return True
elif type(other) == type(self):
return other.__mark == self.__mark
else:
return False
[docs] def __repr__(self):
"""
Overview:
When you try to print a :class:`SingletonMark` object, its mark content will be \
displayed.
Examples::
>>> mark1 = SingletonMark('mark1')
>>> print(mark1)
<SingletonMark mark1>
"""
return "<{cls} {mark}>".format(
cls=self.__class__.__name__,
mark=repr(self.__mark),
)