Search code examples
pythonpython-3.xmetaprogramming

Destroying a Singleton object in Python


I have a Singleton object in Python:

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]

    @classmethod
    def destroy(cls):
        del cls._instances[cls]


class MockObject(metaclass=Singleton):

    def __init__(self, *args, **kwargs):
        # various things

I would like to destroy the object at some point, so I wrote a classmethod in the metaclass. However, the cls refers to the metaclass Singleton rather than MockObject. Is there a way to call the destroy function with a value of MockObject?


Solution

  • Instead of defining a custom method for deleting the instance reference use a WeakValueDictionary.

    Now when there are no more references of MockObject anywhere it will be cleaned up from Singleton._instances automatically.

    from weakref import WeakValueDictionary
    
    
    class Singleton(type):
        _instances = WeakValueDictionary()
    
        def __call__(cls, *args, **kwargs):
            if cls not in cls._instances:
                # This variable declaration is required to force a
                # strong reference on the instance.
                instance = super(Singleton, cls).__call__(*args, **kwargs)
                cls._instances[cls] = instance
            return cls._instances[cls]
    
    
    class MockObject(metaclass=Singleton):
    
        def __init__(self, *args, **kwargs):
            pass
    
    
    if __name__ == '__main__':
        m = MockObject()
        print(dict(Singleton._instances))
        del m
        print(dict(Singleton._instances))
    

    Output:

    {<class '__main__.MockObject'>: <__main__.MockObject object at 0x104531128>}
    {}