Source code for hbutils.design.singleton
"""
Overview:
Implementation of singleton design pattern.
This module provides metaclasses and utilities for implementing singleton patterns in Python.
It includes:
- :class:`SingletonMeta`: A metaclass for creating traditional singleton classes
- :class:`ValueBasedSingletonMeta`: A metaclass for creating value-based singleton classes
- :class:`SingletonMark`: A utility class for creating unique singleton marker objects
"""
__all__ = [
'SingletonMeta',
'ValueBasedSingletonMeta',
'SingletonMark',
]
[docs]
class SingletonMeta(type):
"""
Meta class for singleton mode.
This metaclass ensures that only one instance of a class is created throughout
the application's lifetime. Subsequent calls to the class constructor will
return the same instance.
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):
"""
Override the call method to control instance creation.
:return: The singleton instance of the class.
:rtype: object
"""
if cls not in cls._instances:
cls._instances[cls] = super(SingletonMeta, cls).__call__()
return cls._instances[cls]
[docs]
class ValueBasedSingletonMeta(type):
"""
Meta class for value based singleton mode.
This metaclass creates singleton instances based on a value parameter.
Multiple calls with the same value will return the same instance, while
different values will create different instances.
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):
"""
Override the call method to control instance creation based on value.
:param value: The value used as key for singleton instance lookup.
:type value: Any hashable type
:return: The singleton instance associated with the given value.
:rtype: object
"""
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):
"""
Singleton mark for some situation.
Can be used when some default value is needed, especially when `None` has
meaning which is not default. This class creates unique marker objects that
can be used as sentinel values.
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: str):
"""
Constructor of :class:`SingletonMark`, can create a singleton mark object.
:param mark: The string identifier for this mark.
:type mark: str
"""
self.__mark = mark
@property
def mark(self) -> str:
"""
Get mark string of this mark object.
:return: Mark string identifier.
:rtype: str
"""
return self.__mark
[docs]
def __hash__(self) -> int:
"""
Compute hash value for the singleton mark.
:class:`SingletonMark` objects are hash supported and can be directly used \
in :class:`dict` and :class:`set`.
:return: Hash value of the mark.
:rtype: int
"""
return hash(self.__mark)
[docs]
def __eq__(self, other) -> bool:
"""
Compare equality between singleton marks.
:class:`SingletonMark` objects can be directly compared with ``==``.
:param other: The object to compare with.
:type other: Any
:return: True if marks are equal, False otherwise.
:rtype: bool
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) -> str:
"""
Return string representation of the singleton mark.
When you try to print a :class:`SingletonMark` object, its mark content will be \
displayed.
:return: String representation of the mark.
:rtype: str
Examples::
>>> mark1 = SingletonMark('mark1')
>>> print(mark1)
<SingletonMark 'mark1'>
"""
return "<{cls} {mark}>".format(
cls=self.__class__.__name__,
mark=repr(self.__mark),
)