You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:04:55