Python类属性中的"共享列表"陷阱:为什么你的朋友成了别人的朋友?
· 阅读需 3 分钟
今天给大家分享一个Python中非常容易踩坑的特性——类属性中默认值为可变对象(比如列表)时的诡异行为。
一个看似简单的例子
我们先看一个简单的Person类定义:
class Person:
def __init__(self, name:str, frients:list=[]):
self.name = name
self.frients = frients
def show_frients(self):
print(f"{self.name}, frients: {self.frients}")
看起来很正常对吧?我们创建一个Person实例时,如果没有提供frients参数,就会默认使用空列表。
诡异的现象出现了
让我们创建两个Person实例:
def main():
zhangsan = Person("zhangsan")
lisi = Person("lizi")
zhangsan.frients.append("x")
zhangsan.show_frients()
lisi.show_frients()
运行结果会让你大吃一惊:
zhangsan, frients: ['x']
lizi, frients: ['x']
什么?我们只给张三添加了朋友"x",为什么李四也自动拥有了这个朋友?
为什么会这样?
这个现象看似诡异,但其实有合理的解释:
- 默认参数在函数定义时就被求值:Python的函数默认参数是在函数定义时(而不是调用时)就创建的
- 所有实例共享同一个默认列表:因为没有显式传入
frients参数,两个实例都使用了同一个默认列表对象 - 修改的是同一个列表:当通过一个实例修改列表时,所有使用默认列表的实例都会受到影响
如何避免这个坑?
方法1:显式传入空列表
def main():
zhangsan = Person("zhangsan", [])
lisi = Person("lizi", [])
zhangsan.frients.append("x")
zhangsan.show_frients()
lisi.show_frients()
这种方法可行,但每次创建实例都要写个[],失去了默认参数的意义。
方法2:修改类定义(推荐)
更好的方法是在类定义时就避免这个问题:
class Person:
def __init__(self, name:str, frients:list|None=None):
self.name = name
if frients is None:
self.frients = []
else:
self.frients = frients
这样:
- 默认参数使用
None(不可变对象) - 在
__init__中根据情况创建新列表 - 每个实例都会得到自己独立的列表
PyCharm的贴心提示
如果你使用PyCharm,它会智能地识别出这个潜在问题并给出警告:

接受它的建议就能自动修复这个问题。
总结
这个"坑"其实体现了Python的一个设计选择:默认参数在函数定义时求值。对于不可变对象(如数字、字符串、元组)这不是问题,但对于可变对象(如列表、字典)就可能引发意外行为。
记住这个原则:永远不要用可变对象作为函数/方法的默认参数,用None代替并在函数内部初始化。
希望这篇文章能帮你避开这个Python新手常见的陷阱!
