python函数默认参数在定义时只求值一次,故list/dict作默认值会导致所有调用共享同一对象;应使用none占位并在函数体内初始化,避免数据污染。

为什么list或dict不能直接当默认参数
Python函数(包括__init__)的默认参数在定义时只求值一次,不是每次调用都新建。如果用list或dict作默认值,所有实例会共享同一个对象,导致意外的数据污染。
比如:
class Cache:
def __init__(self, items=[]): # 危险!
self.items = items
连续创建两个Cache()实例后,对其中一个.items.append(),另一个也会看到新增项。用None占位 + 类型检查是标准解法
把默认值设为None,在方法体内显式判断并初始化,确保每次调用都获得独立副本。
- 写法统一、意图清晰,符合PEP 8和主流代码库惯例
- 避免隐式共享,也规避了
is与==混淆风险 - 支持传入
None作为合法业务值(如“明确不设置缓存项”)时,需额外约定或改用sentinel对象
示例:
class Cache:
def __init__(self, items=None):
self.items = items if items is not None else []
items or []看起来简洁,但有陷阱
用or短路表达式看似省事,但会误判“falsy”值,比如传入空字符串""、数字0、False时,都会被当成None处理,触发默认初始化。
- 仅当确认参数只接受容器类型且不会传入其他falsy值时才可考虑
- 多数场景下,
is not None更安全、语义更准确 - 若需支持
None和[]都表示“无初始数据”,则必须显式区分判断
类属性 vs 实例属性:别在类定义里直接赋值可变对象
即使不用默认参数,也要警惕在类体中直接写items = []——这会让所有实例共享该列表。
- 类属性用于常量或真正需要共享的状态(如计数器)
- 所有可变状态必须在
__init__中通过self.xxx = ...绑定到实例 - IDE和类型检查器(如mypy)通常能识别这类错误,但运行时不会报错
错误示范:
class BadCache:
items = [] # 所有实例共用这个list!
def __init__(self):
pass
可变默认参数的问题不在语法层面,而在于对象生命周期的理解偏差。只要记住“默认参数只初始化一次”,就自然会倾向用None占位,并在函数体内做干净的实例化。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











