Python:Singleton
lastmod 2024-04-11created 2021-11-245 edits
The Singleton pattern implemented in Python.
Method type
Of the examples here, this one has the fastest call speed and the fewest problems with various inheritance setups and other situations. It is used much like the common C/C++ approach: you call the instance method directly. The class can also come last in the inheritance order, so this seems to be the best method.
class SingletonInstane:
__instance = None
@classmethod
def __getInstance(cls):
return cls.__instance
@classmethod
def instance(cls, *args, **kargs):
cls.__instance = cls(*args, **kargs)
cls.instance = cls.__getInstance
return cls.__instance
class MyClass(BaseClass, SingletonInstane):
pass
c = MyClass.instance()
A decorator
Fast, but calling the class itself returns a function, so you cannot access the class's StaticMethods..
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
A base class
Very slightly slower than the decorator-style singleton, but the most general-purpose because it can be used like a regular class.
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
A metaclass
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
Decorator returning a class with the same name
A version used as a decorator that overwrites the class name (the __name__ attribute) and fixes the object.__new__(cls) error:
# -*- coding: utf-8 -*-
from inspect import signature
def singleton(base):
class _Wrapper(base):
__singleton_instance__ = None
def __new__(cls, *args, **kwargs):
if _Wrapper.__singleton_instance__ is None:
super_cls = super(_Wrapper, cls)
if object.__new__ == super_cls.__new__:
instance = super_cls.__new__(cls)
else:
new_sig = signature(super_cls.__new__)
ba = new_sig.bind(cls, *args, **kwargs)
ba.apply_defaults()
instance = super_cls.__new__(*ba.args, **ba.kwargs)
_Wrapper.__singleton_instance__ = instance
_Wrapper.__singleton_instance__.__singleton_sealed__ = False
return _Wrapper.__singleton_instance__
def __init__(self, *args, **kwargs):
if self.__singleton_sealed__:
return
super(_Wrapper, self).__init__(*args, **kwargs)
self.__singleton_sealed__ = True
@staticmethod
def get_singleton_instance() -> base:
return _Wrapper.__singleton_instance__
_Wrapper.__name__ = base.__name__
return _Wrapper
Unit tests:
# -*- coding: utf-8 -*-
from unittest import TestCase, main
from cw.patterns.singleton import singleton
@singleton
class _TestNoInheritance:
def __init__(self, value=999):
self.value = value
@classmethod
def name(cls):
return cls.__name__
class _TestBase:
def __new__(cls, value=888):
return object.__new__(cls)
def __init__(self, value=999):
self.value = value
@classmethod
def name(cls):
return cls.__name__
@singleton
class _TestInheritance(_TestBase):
def __init__(self, value=999):
super().__init__(value)
@classmethod
def name(cls):
return cls.__name__
class SingletonTestCase(TestCase):
def test_no_inheritance(self):
o0 = _TestNoInheritance(100)
o1 = _TestNoInheritance(200)
o2 = _TestNoInheritance(300)
o3 = _TestNoInheritance()
self.assertTrue(hasattr(o0, "__singleton_instance__"))
self.assertTrue(hasattr(o1, "__singleton_instance__"))
self.assertTrue(hasattr(o2, "__singleton_instance__"))
self.assertTrue(hasattr(o3, "__singleton_instance__"))
self.assertIsNotNone(getattr(o0, "__singleton_instance__"))
self.assertIsNotNone(getattr(o1, "__singleton_instance__"))
self.assertIsNotNone(getattr(o2, "__singleton_instance__"))
self.assertIsNotNone(getattr(o3, "__singleton_instance__"))
self.assertTrue(hasattr(o0, "__singleton_sealed__"))
self.assertTrue(hasattr(o1, "__singleton_sealed__"))
self.assertTrue(hasattr(o2, "__singleton_sealed__"))
self.assertTrue(hasattr(o3, "__singleton_sealed__"))
self.assertEqual(o0.value, 100)
self.assertEqual(o1.value, 100)
self.assertEqual(o2.value, 100)
self.assertEqual(o3.value, 100)
self.assertEqual(o0.name(), "_TestNoInheritance")
self.assertEqual(o1.name(), "_TestNoInheritance")
self.assertEqual(o2.name(), "_TestNoInheritance")
self.assertEqual(o3.name(), "_TestNoInheritance")
self.assertEqual(id(o0), id(o1))
self.assertEqual(id(o0), id(o2))
self.assertEqual(id(o0), id(o3))
def test_inheritance(self):
o0 = _TestInheritance(100)
o1 = _TestInheritance(200)
o2 = _TestInheritance(300)
o3 = _TestInheritance()
self.assertTrue(hasattr(o0, "__singleton_instance__"))
self.assertTrue(hasattr(o1, "__singleton_instance__"))
self.assertTrue(hasattr(o2, "__singleton_instance__"))
self.assertTrue(hasattr(o3, "__singleton_instance__"))
self.assertIsNotNone(getattr(o0, "__singleton_instance__"))
self.assertIsNotNone(getattr(o1, "__singleton_instance__"))
self.assertIsNotNone(getattr(o2, "__singleton_instance__"))
self.assertIsNotNone(getattr(o3, "__singleton_instance__"))
self.assertTrue(hasattr(o0, "__singleton_sealed__"))
self.assertTrue(hasattr(o1, "__singleton_sealed__"))
self.assertTrue(hasattr(o2, "__singleton_sealed__"))
self.assertTrue(hasattr(o3, "__singleton_sealed__"))
self.assertEqual(o0.value, 100)
self.assertEqual(o1.value, 100)
self.assertEqual(o2.value, 100)
self.assertEqual(o3.value, 100)
self.assertEqual(o0.name(), "_TestInheritance")
self.assertEqual(o1.name(), "_TestInheritance")
self.assertEqual(o2.name(), "_TestInheritance")
self.assertEqual(o3.name(), "_TestInheritance")
self.assertEqual(id(o0), id(o1))
self.assertEqual(id(o0), id(o2))
self.assertEqual(id(o0), id(o3))
if __name__ == "__main__":
main()