使用类变量与字典存储实例时,单例模式为何表现不同?
两种Base基类实现导致子类实例行为差异的原因分析
第一种实现的逻辑与结果
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
原因
Python中,子类继承父类的类属性时,当子类对类属性进行赋值操作,会在子类自己的命名空间中创建独立的属性副本。
在这个实现里,__new__方法中操作的是cls._instance——cls是当前被实例化的子类(A或B)。实例化A时,A没有自己的_instance属性,会先读取Base的_instance(值为None),随后给A._instance赋值;实例化B时,同理操作的是B._instance。最终A和B各自拥有独立的实例存储,所以a和b是两个不同的对象,ID不同。
第二种实现的逻辑与结果
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
原因
这里的_instances是Base类的字典属性,子类A和B不会自动创建该属性的副本——因为代码中只对字典进行in判断和内部键值对赋值操作,没有直接给cls._instances重新赋值。
实例化A时,cls是A,A没有自己的_instances,会直接使用Base的_instances字典。此时字典中没有0这个键,所以创建A的实例存入Base._instances[0]。当实例化B时,cls是B,同样会读取Base的_instances字典,此时0键已经存在,直接返回字典里的A实例,所以b和a是同一个对象,ID完全相同。
内容的提问来源于stack exchange,提问作者Victor Wong
相关产品推荐
相关产品推荐

