首先,我们先来看现象,再来分析原因。
191class ShoppingCart:2 def __init__(self, owner, items=[], total_price=0): # 用[]做默认值3 self.owner = owner4 self.items = items5 self.total_price = total_price6
7 def add_item(self, item_name):8 self.items.append(item_name)9
10# 创建2个购物车实例11cart1 = ShoppingCart("小明")12cart2 = ShoppingCart("小红")13
14# 只给cart1加物品15cart1.add_item("小米手机")16
17# 打印两个购物车的items18print("小明的购物车:", cart1.items) # 输出:['小米手机']19print("小红的购物车:", cart2.items) # 输出:['小米手机'] 小红的购物车也有这个物品!
我创建了两个购物车的实例且只给其中的一个对象的items里,添加了小米手机这一个值,但是为什么两个对象的实例变量里都有同样的值呢?
核心原因:Python解释器处理代码的时候会按 定义阶段和 调用阶段两步走:
当解释器读到 def __init__(self, owner, items=[], total_price=0): 时,会:
为默认参数 items=[] 创建一个全局唯一的空列表对象(内存地址固定);
把这个列表对象绑定到 items 默认参数上。
每次执行 ShoppingCart("小明")/ShoppingCart("小红") 时:
因为没传 items 参数,会复用定义阶段创建的那个全局列表;
所有实例的 self.items 都指向同一个内存地址的列表;
所以修改任意一个实例的 items,其他实例的 items 都会跟着变。
如果听起来还是比较模糊的话,我们用更直观的内存地址验证一下,就能看到本质:
91class ShoppingCart:2 def __init__(self, owner, items=[], total_price=0):3 self.owner = owner4 self.items = items5 print(f"{owner}的items内存地址:{id(self.items)}") # 打印内存地址6
7
8cart1 = ShoppingCart("小明") # 小明的items内存地址:43051247369cart2 = ShoppingCart("小红") # 小红的items内存地址:4305124736两个实例的 items 指向同一个内存地址,自然会共享数据。
解决思路:默认值用不可变对象(比如 None),在方法内部创建新的可变对象!
191class ShoppingCart:2 def __init__(self, owner, items=None, total_price=0):3 self.owner = owner4 # 如果没传items,就创建新的空列表(每次调用都新建,地址不同)5 self.items = items if items is not None else []6 self.total_price = total_price7 def add_item(self, item_name):8 self.items.append(item_name)9
10# 验证11cart1 = ShoppingCart("小明")12cart2 = ShoppingCart("小红")13
14print(f"小明的items地址:{id(cart1.items)}") # 14068252590016015print(f"小红的items地址:{id(cart2.items)}") # 140682525900224 ✅ 地址不同!16
17cart1.add_item("小米手机")18print("小明的购物车:", cart1.items) # ['小米手机']19print("小红的购物车:", cart2.items) # [] ✅ 互不影响!
永远不要用列表、字典等可变对象作为方法的默认参数,用 None 代替,在内部创建新对象就不会踩坑了!!!!
这里在复习一下,不可变对象有哪些,其实就是str和tuple ,这是我们常用的!