Skip to content
Search

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()

Favorite site

author Zer0 sitemap