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
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.