Python类属性中的"共享列表"陷阱:为什么你的朋友成了别人的朋友?
今天给大家分享一个Python中非常容易踩坑的特性——类属性中默认值为可变对象(比如列表)时的诡异行为。
一个看似简单的例子¶
我们先看一个简单的Person
类定义:
看起来很正常对吧?我们创建一个Person
实例时,如果没有提供frients
参数,就会默认使用空列表。
诡异的现象出现了¶
让我们创建两个Person
实例:
运行结果会让你大吃一惊:
什么?我们只给张三添加了朋友"x",为什么李四也自动拥有了这个朋友?
为什么会这样?¶
这个现象看似诡异,但其实有合理的解释:
- 默认参数在函数定义时就被求值:Python的函数默认参数是在函数定义时(而不是调用时)就创建的
- 所有实例共享同一个默认列表:因为没有显式传入
frients
参数,两个实例都使用了同一个默认列表对象 - 修改的是同一个列表:当通过一个实例修改列表时,所有使用默认列表的实例都会受到影响
如何避免这个坑?¶
方法1:显式传入空列表¶
这种方法可行,但每次创建实例都要写个[]
,失去了默认参数的意义。
方法2:修改类定义(推荐)¶
更好的方法是在类定义时就避免这个问题:
这样:
- 默认参数使用None
(不可变对象)
- 在__init__
中根据情况创建新列表
- 每个实例都会得到自己独立的列表
PyCharm的贴心提示¶
如果你使用PyCharm,它会智能地识别出这个潜在问题并给出警告:
接受它的建议就能自动修复这个问题。
总结¶
这个"坑"其实体现了Python的一个设计选择:默认参数在函数定义时求值。对于不可变对象(如数字、字符串、元组)这不是问题,但对于可变对象(如列表、字典)就可能引发意外行为。
记住这个原则:永远不要用可变对象作为函数/方法的默认参数,用None
代替并在函数内部初始化。
希望这篇文章能帮你避开这个Python新手常见的陷阱!
