Search code examples
pythonsingleton

Singleton different behavior when using class and dict to store the instance


Why do these two base classes result in the child objects having different behavior?

class Base:

    _instance: "Base" = None

    def __new__(cls) -> "Base":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

class A(Base):
    def foo(self):
        return "foo"

class B(Base):
    def quz(self):
        return "quz"

a = A()
b = B()

print(id(a))
print(id(b))
140035075937792
140035075948400

On the other hand

from typing import Dict

class Base:

    _instances: Dict[int, "Base"] = {} 

    def __new__(cls) -> "Base":
        if 0 not in cls._instances:
            cls._instances[0] = super().__new__(cls)
        return cls._instances[0]

class A(Base):
    def foo(self):
        return "foo"

class B(Base):
    def quz(self):
        return "quz"

a = A()
b = B()

print(id(a))
print(id(b))
140035075947296
140035075947296

Solution

  • When a = A() is executed, the __new__ method is called with the class A as its argument. This sets the value of the class attribute A._instance. Likewise, b = B() sets the value of B._instance.

    In the first case, the original value of Base._instance, A.instance and B._instance is None, which is a non-mutable object, so changing this value in A or B does not affect the other two classes.

    In the second case, A._instance, B._instance and Base._instance point to the same dictionary. Since a dictionary is a mutable object, modifying this dictionary via one class affect all three classes.