空对象模式的核心目的是统一接口,即让“不存在”表现为实现相同接口的真实对象,所有方法返回安全默认值,使调用方无需判空;适用于可选依赖或降级场景,但不可用于掩盖应报错的故障或副作用操作。

空对象模式的核心目的不是“消除None”,而是“统一接口”
直接用 None 做占位,后续必须写一堆 if obj is not None:,这本质是把控制流逻辑散落在各处。空对象模式的要点是:让“不存在”也表现为一个**真实对象**,它实现和正常对象相同的接口,但所有方法都做“无操作”或返回安全默认值(如空列表、0、空字符串)。这样调用方完全不用关心对象是否存在。
Python中实现空对象的关键是协议一致性
空对象必须能通过鸭子类型被正常调用——比如你有个 User 类有 .name、.get_profile()、.is_active,那空对象就得有同名属性和方法,且不抛异常、不改变状态。常见错误是只实现了部分方法,或者返回了错误类型(例如 .get_profile() 返回 None 而不是空字典)。
- 推荐继承原类(如果可修改原类),或至少实现
__class__和__dict__以骗过类型检查工具 - 属性访问建议用
@property或__getattr__统一兜底,避免漏定义字段 - 方法体里尽量不写
pass,而是显式返回语义安全的值:return []、return ""、return False
NullCacheClient
- 涉及副作用的操作(保存、发送、扣款)绝不能用空对象吞掉
- 需要区分“未找到”和“找到但为空”的场景,空对象会抹平这个差异
- 调试时容易忽略空对象的存在,建议在
__repr__里明确标出:"NullUser()"
用工厂函数封装空对象创建,避免硬编码判断
不要在业务代码里写 user = find_user(id) or NullUser(),这又把 None 检查拉回来了。应该把判空逻辑收进工厂,让调用方只管拿对象:
def get_user_or_null(user_id):
user = db.query(User).filter(User.id == user_id).first()
return user if user else NullUser()
<h1>使用时干净利落</h1><p>user = get_user_or_null(123)
print(user.name) # 安全,不会 AttributeError
user.send_welcome_email() # NullUser.send_welcome_email() 什么也不做</p>
更进一步,可用注册表支持多类型空对象:get_object("user", 123) 自动返回对应 NullUser 或 NullOrder,避免到处 new。
空对象模式真正难的不是写那个 NullXxx 类,而是想清楚:这个“空”在当前上下文中,到底该表现为什么行为?返回空列表还是抛异常?记录日志还是静默跳过?这些决策一旦定错,后面排查成本远高于多写几个 if。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











