单例+懒加载最自然的实现是通过__getattr__拦截未定义属性,在其中检查实例是否存在并按需创建缓存;结合__dict__直接访问、property伪只读封装及cached_property线程安全机制可规避竞态风险。

直接在属性访问时检查实例是否已存在,不存在就创建并缓存,存在就直接返回——这是实现单例+懒加载最自然的方式。
用 __getattr__ 拦截未定义属性
当访问一个尚未初始化的单例属性时,Python 会调用 __getattr__。我们可以在这里做存在性检查和按需创建:
- 先尝试从实例字典 self.__dict__ 中获取目标属性
- 若不存在,调用工厂方法(如 create_service())生成实例
- 将结果存入 self.__dict__ 并返回,后续访问直接命中缓存
避免重复初始化:用 hasattr 或字典键检查
在常规属性访问逻辑中(比如在方法里),可用 hasattr(self, '_cache') 判断是否已构建;更稳妥的是直接查 self.__dict__.get('_cache'):
- hasattr 可能触发 property 的 getter,带来副作用
- 直接查 __dict__ 绕过 descriptor 机制,确保只看真实存储状态
- 适合在 __init__ 外部或延迟初始化入口处使用
结合 property 实现“伪只读”懒加载属性
定义一个 property,在其 getter 中完成存在检查与创建:
- 内部用 object.__getattribute__(self, '_client') 尝试获取(绕过自身 getter 递归)
- 捕获 AttributeError 表示未初始化,此时创建并设置 self._client = ...
- 返回已存在的实例,对外呈现为普通属性访问
注意线程安全与多次赋值风险
单例懒加载在多线程下可能被多次初始化,尤其在 if not hasattr 到赋值之间存在竞态窗口:
- 简单场景可用 threading.Lock 包裹初始化块
- 更推荐用 functools.cached_property(Python 3.8+),它自带线程安全与原子写入
- 避免在 getter 中直接写 self._x = create_x() 而不加锁,否则可能覆盖他人刚写入的实例

















