应使用None作默认参数并在函数内调用工厂函数生成新对象,避免可变默认参数陷阱;因默认参数在定义时求值一次,直接写func(x=[])会导致多次调用共享同一对象。

为什么不能直接写 func(x = [])
Python 中默认参数在函数定义时就求值一次,不是每次调用时新建。所以 def f(x = []) 的 [] 是同一个列表对象,多次调用会累积修改——这是经典陷阱。工厂函数本身不解决这个问题,除非你让它“延迟执行”。
正确做法:用 None 作占位符 + 函数内调用工厂
把默认值设为 None,在函数体里判断并调用工厂函数生成新对象。这是最清晰、最易读、兼容所有 Python 版本的方式。
示例:
def create_item(items=None):
if items is None:
items = list() # 或 lambda: [], 或其他工厂逻辑
items.append("new")
return items
- 工厂逻辑可以是
list()、dict()、MyClass(),也可以是自定义函数如lambda: [0] * 3 - 避免用
is []或== []判断,默认值是None才可靠 - 如果工厂开销大(比如读文件、建连接),这个模式也天然支持按需创建
想更“声明式”?封装一个装饰器或基类
如果你大量函数都需要这种行为,可以抽象一层,但别过早优化。简单场景下,硬编码 if x is None: x = factory() 更直白、调试更友好。
装饰器示例(仅作参考,非推荐首选):
def lazy_default(**factories):
def decorator(func):
def wrapper(**kwargs):
for k, factory in factories.items():
if k not in kwargs or kwargs[k] is None:
kwargs[k] = factory()
return func(**kwargs)
return wrapper
return decorator
@lazy_default(items=lambda: [])
def process(items):
items.append("done")
return items
- 这个装饰器只处理关键字参数,且要求调用时显式传
items=None才触发工厂 - 它掩盖了参数的实际初始化时机,对 IDE 类型推导、静态检查都不友好
- 一旦工厂函数抛异常,堆栈会变深,debug 成本上升
注意:不要用 functools.partial 模拟工厂默认值
有人试图写 def f(x=partial(list)),这是错的——partial 对象本身不是可调用的“结果”,而是一个待绑定的对象;你得手动 x() 才能拿到新列表,这破坏了函数签名语义。
-
partial(list)不等于list(),前者是构造器,后者是调用结果 - 类型提示(如
x: list = partial(list))会失效,mypy 会报错 - 调用方无法直观理解这个参数“应该传什么”,可维护性差
真正需要每次新建,默认值必须延迟到函数体内执行。最安全的路径就是守住 None 占位 + 显式工厂调用这条线。其它包装方案在多数项目里只是增加认知负担。










