我们知道Python中函数的可选参数可以有默认值,但是该默认值不能是可变类型。下面以一个简单的例子简单说明一下。
class HauntedBus:
"""闹鬼的校车"""
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
"""上车"""
self.passengers.append(name)
def drop(self, name):
"""下车"""
self.passengers.remove(name)
bus1 = HauntedBus()
bus1.pick('Tom')
print(bus1.passengers) # ['Tom']
bus2 = HauntedBus() # ['Tom']
print(bus2.passengers)
bus2.pick('Jack')
print(bus2.passengers) # ['Tom', 'Jack']
print(bus1.passengers is bus2.passengers) # True
从结果中可以看出,登上bus1的乘客Tom出现在了bus2中。
问题在于,没有指定初始乘客的 HauntedBus 实例会共享同一个乘客列表。
这是因为不为 HauntedBus
指定乘客的话self.passengers
变成了 passengers
参数默认值(即[]
)的别名,而这个问题的根源在于默认值在定义函数时计算(通常在加载模块时),就已经变成了函数对象的属性。因此,如果默认值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影响。
可变默认值导致的这个问题说明了为什么通常使用 None 作为接收可变值的参数的默认值。
下面修改上面例子中的部分问题代码。
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers)
在这个代码中,如果构造实例时没有传入参数,那么会新建一个空列表赋值给self.passengers
,另外值得注意的是,如果参数passengers不为None,我们将参数值的副本list(passengers)
赋值给了self.passengers
。这是因为如果直接将passengers
赋值给self.passengers
的话,那么在self.passengers
上直接调用remove()
或者append()
方法的话,其实会修改传给构造方法的那个列表。
网友评论