如何避免Python单例场景下B类__init__方法被多次调用?
单例实现优化:避免重复调用
__init__ 已实现目标
- 通过
A()和B()创建的实例均为B类实例; - B类为单例,两种方式创建的实例
id完全相同。
待解决问题
案例1中B类的__init__方法被调用3次,案例2中被调用2次。需求是__init__仅在首次创建实例(无论是通过A()还是B())时调用一次,且无需在__init__内添加初始化判断。
相关代码(原实现)
class A: _default_impl = None _instance = None @classmethod def set_default_impl(cls, impl_class): cls._default_impl = impl_class def __new__(cls, *args, **kwargs): if cls is A: if cls._default_impl is not None: if cls._default_impl._instance is None: cls._default_impl._instance = cls._default_impl(*args, **kwargs) return cls._default_impl._instance else: raise TypeError("No implementation is set") else: if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance class B(A): def foo(self): print("Class B implementation") def __init__(self): print("init called") A.set_default_impl(B) # Case 1: obj1 = A() obj2 = A() obj2.foo() # prints: Class B implementation print("same") if id(obj1) == id(obj2) else print("Not same") # Case 2: obj1 = B() obj2 = B() obj2.foo() # prints: Class B implementation print("same") if id(obj1) == id(obj2) else print("Not same")
现有临时解决方案
class B(A): _initialzed = False def foo(self): print("Class B implementation") def __init__(self): if not self._initialzed: self._initialzed = True print("init called")
优化解决方案(无需修改B类__init__)
核心思路是重写类的__call__方法——Python中调用类()本质是调用类的__call__方法,该方法默认会依次执行__new__和__init__。通过重写__call__,我们可以控制仅在首次创建实例时执行完整的实例化流程,后续直接返回已有的单例实例,避免重复调用__init__。
class A: _default_impl = None _instance = None @classmethod def set_default_impl(cls, impl_class): cls._default_impl = impl_class def __new__(cls, *args, **kwargs): # 仅负责创建实例,单例逻辑交由__call__处理 return super().__new__(cls) @classmethod def __call__(cls, *args, **kwargs): if cls is A: if cls._default_impl is None: raise TypeError("No implementation is set") # 检查B类的单例实例是否存在 if cls._default_impl._instance is None: # 首次创建时调用默认的__call__,触发__new__和__init__ cls._default_impl._instance = super(A, cls._default_impl).__call__(*args, **kwargs) return cls._default_impl._instance else: # 处理B类自身的单例逻辑 if cls._instance is None: cls._instance = super().__call__(*args, **kwargs) return cls._instance class B(A): def foo(self): print("Class B implementation") def __init__(self): print("init called") A.set_default_impl(B) # Case 1 测试 print("=== Case 1 ===") obj1 = A() obj2 = A() obj2.foo() print("same") if id(obj1) == id(obj2) else print("Not same") # Case 2 测试 print("\n=== Case 2 ===") obj1 = B() obj2 = B() obj2.foo() print("same") if id(obj1) == id(obj2) else print("Not same")
效果验证
运行上述代码后,无论是A()还是B()的调用,__init__仅会在首次创建实例时打印一次"init called",后续调用不会再触发,同时单例特性保持不变。
内容的提问来源于stack exchange,提问作者Ashin K Ajay
相关产品推荐
相关产品推荐

