None哨兵模式与默认参数最佳实践
请深入讲解Python中None哨兵(sentinel)模式的使用场景和最佳实践。除了None,为什么有时需要用自定义哨兵对象?如何处理不希望None作为合法值传入的情况?
回答
Yahuda
None哨兵模式
默认参数处理可变对象的标准模式:
def process(data, config=None):
if config is None:
config = {}
config.update(data)
return config
为什么有时需要自定义哨兵?
当None本身是合法的参数值时,需要自定义哨兵对象:
_MISSING = object() # 自定义哨兵
def get_user(user_id, cache=_MISSING):
if cache is _MISSING:
cache = {} # 不传时使用新字典
# cache可以是{}或None
if cache is not None and user_id in cache:
return cache[user_id]
return db_lookup(user_id, cache=cache)
使用场景:
cache=None表示禁用缓存cache={}使用空缓存- 不传参时使用新字典
更多哨兵模式
# 可变默认值的更安全模式
from dataclasses import dataclass, field
@dataclass
class Config:
items: list = field(default_factory=list) # 每次调用生成新列表
最佳实践总结
- 可变默认参数 -> 使用
None哨兵 - None是合法值 -> 使用自定义
_MISSING = object() - dataclass -> 使用
field(default_factory=...) - 类型提示 -> 使用
Optional[T]或Union[T, type(None)]明确标注