__missing__ 方法仅在继承 dict 的子类中有效,且仅通过 [] 访问缺失键时触发,返回值会被自动缓存;它支持基于 key 的差异化逻辑,但需确保正确调用 super().__getitem__(key) 才能生效。

__missing__ 方法只在继承 dict 时生效
直接给普通 dict 实例设置 __missing__ 方法无效——Python 的内置 dict 不会调用它。必须通过继承 dict 创建子类,且仅当使用 [](即 __getitem__)访问缺失键时触发。
常见错误是试图对已有字典动态添加 __missing__,比如:d = {} → d.__missing__ = lambda k: "default",这完全没用。
- 必须定义为子类的实例方法,签名固定为
def __missing__(self, key): - 仅对
d[key]触发;d.get(key)、key in d等不走这条路 - 如果方法未返回值或返回
None,仍会抛出KeyError
__missing__ 和 defaultdict 的关键区别
defaultdict 在构造时指定默认工厂函数,所有缺失键都统一处理;而 __missing__ 是运行时按需计算,可基于 key 做差异化逻辑,比如路由分发或类型推导。
例如:想让键是字符串时返回空列表,是数字时返回 0,用 defaultdict 很难干净实现,但 __missing__ 可直接判断:
class SmartDict(dict):
def __missing__(self, key):
if isinstance(key, str):
return []
elif isinstance(key, int):
return 0
raise KeyError(key)
-
defaultdict的工厂函数无法访问 key,__missing__可以 -
defaultdict对每个缺失键都调用工厂,__missing__可缓存、查表、甚至抛定制异常 - 二者都不影响已存在的键,也不改变
dict的其他行为
别忘了显式调用父类 __getitem__ 才能触发 __missing__
如果子类重写了 __getitem__ 却没调用 super().__getitem__(key),__missing__ 就永远不会执行——这是最常被忽略的坑。
正确写法是:在自定义 __getitem__ 中,先尝试父类获取,捕获 KeyError 后再委托给 __missing__,或者更简单:直接调用 super().__getitem__(key),它内部已封装了 __missing__ 调用逻辑。
- 错误示范:
def __getitem__(self, key): return self.data.get(key, "fallback")→ 绕过整个机制 - 正确做法:
def __getitem__(self, key): return super().__getitem__(key) - 若需前置逻辑(如日志),应放在
super().__getitem__(key)之前,而非替换它
__missing__ 返回值会被缓存到字典中
这是隐含但关键的行为:__missing__ 的返回值会自动赋给该 key,后续访问直接命中,不再调用 __missing__。相当于“懒初始化 + 自动插入”。
这意味着不能依赖 __missing__ 总是重新计算——比如返回时间戳或随机数,第一次调用后就固化了。需要动态值时,得手动控制是否写入:
class DynamicDict(dict):
def __missing__(self, key):
value = f"computed_{key}_{id(self)}"
# 不调用 self[key] = value,避免缓存
return value
- 只要方法体里没做
self[key] = ...或super().__setitem__(key, ...),就不会缓存 - 但如果用了
super().__getitem__(key)触发机制,且__missing__返回了值,Python 自动完成赋值 - 这种自动缓存对多数场景是便利,但对状态敏感逻辑(如单次 token 生成)就是陷阱
真正要用好 <strong>missing</strong>,得盯住三点:继承关系是否成立、访问方式是否走 <strong>getitem</strong>、返回值是否无意中被持久化。漏掉任意一个,行为就和预期差很远。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











