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

Python基于__new__实现的单例模式表现异常,求原因解释

单例类继承后的异常行为解析

我发现一种创建单例类的主流实现方式,即使用_instance属性并重写__new__方法,代码如下:

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance

但当我按如下方式使用时,它表现出奇怪的行为:

import random

class Child(Singleton):
    def __init__(self):
        self.a = random.randint(10, 1000)

运行以下代码后:

x = Child()
y = Child()
print(x.__dict__)        # {'a': 74}
print(y.__dict__)        # {'a': 74}  - 这部分符合预期
print(Child().__dict__)  # {'a': 222} - 这部分让人困惑

背后的原因

  1. 单例的核心逻辑:Singleton的__new__方法保证每次调用类构造时,都会返回同一个_instance对象,所以x和y指向的是同一个实例,它们的属性字典自然完全一致。
  2. __init__的执行规则:Python中,每次调用类构造(比如Child())时,不管实例是否已经存在,都会自动执行该实例的__init__方法。__new__负责创建实例,__init__负责初始化实例,二者是独立的步骤。
  3. Child().__dict__变化的本质:当你调用Child()时,返回的还是之前的单例实例,但此时会再次触发__init__方法,重新给self.a赋值为新的随机数。你打印的是执行完这次初始化后的属性字典,所以看到的是新的数值。
  4. 直观验证:如果在打印Child().__dict__后再查看x的属性,会发现x的a值也变成了222——因为它们从始至终都是同一个对象:
x = Child()
print(x.__dict__)        # {'a': 74}
print(Child().__dict__)  # {'a': 222}
print(x.__dict__)        # {'a': 222}

解决思路

如果希望单例实例只被初始化一次,可以在类中添加初始化标记,控制__init__的执行次数。比如修改Singleton基类:

class Singleton:
    _instance = None
    _initialized = False

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not self._initialized:
            # 这里放置需要只执行一次的初始化逻辑
            self._initialized = True

或者在Child子类中添加类似的判断逻辑,确保__init__仅在实例首次创建时执行。

内容的提问来源于stack exchange,提问作者llownall

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:21:15