pythonsingletondecoratorbase-classmetaclass

What is the best way of implementing a singleton in Python?


I have multiple classes which would become singletons (my use case is for a logger, but this is not important). I do not wish to clutter several classes with added gumph when I can simply inherit or decorate.

Best methods:


Method 1: A decorator

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

Pros

Cons

then x == y but x != t && y != t


Method 2: A base 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

Pros

Cons


Method 3: 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

Pros

Cons


Method 4: decorator returning a class with the same name

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

Pros

Cons


Method 5: a module

a module file singleton.py

Pros

Cons

This question is not for the discussion of whether or not the singleton design pattern is desirable, is an anti-pattern, or for any religious wars, but to discuss how this pattern is best implemented in Python in such a way that is most Pythonic. In this instance I define 'most Pythonic' to mean that it follows the 'principle of least astonishment'.


Solution

  • You just need a decorator, different depending on the python version.

    Notice how foo gets printed only once.


    Python 3.2+

    Implementation:

    from functools import lru_cache
    
    @lru_cache(maxsize=None)
    class CustomClass(object):
    
        def __init__(self, arg):
            print(f"CustomClass initialised with {arg}")
            self.arg = arg
    

    Usage

    c1 = CustomClass("foo")
    c2 = CustomClass("foo")
    c3 = CustomClass("bar")
    
    print(c1 == c2)
    print(c1 == c3)
    

    Output

    >>> CustomClass initialised with foo
    >>> CustomClass initialised with bar
    >>> True
    >>> False
    

    Python 3.9+

    Implementation:

    from functools import cache
    
    @cache
    class CustomClass(object):
        ...