如果参数的实际默认值是可变类型(mutable),那就一定要记得用 None 作为形式上的默认值。例如,从编码为 JSON 格式的数据中载入某个值。若解码数据时失败,则默认返回空的字典。我们可能会采用下面这种办法来实现此功能:
import json
def decode(data,default = {}):
try:
json.loads(data)
except ValueError:
return default
foo = decode('bad data')
foo['stuff'] = 'why'
bar = decode('also bad')
bar['meep'] = 'i dunno'
print('Foo',foo)
print('Bar',bar)
由于 default 参数的默认值只会在模块加载时评估一次,所以凡是以默认形式来调用 decode 函数的代码,都将共享同一份字典。
输出结果:
('Foo', {'stuff': 'why', 'meep': 'i dunno'})
('Bar', {'stuff': 'why', 'meep': 'i dunno'})
我们本以为 foo 和 bar 会表示两份不同的字典,每个字典里都有一对键和值,但实际上,修改了其中一个之后,另外一个似乎也会受到影响。这种错误的根本原因是:foo 和 bar 其实都等同于写在 default 参数默认值中的那个字典,它们都表示的是同一个字典对象。
解决办法,是把关键字参数的默认值设为 None,并在函数的文档字符串中描述它的实际行为。
def decode(data,default = None):
if default is None:
default = {}
try:
json.loads(data)
except ValueError:
return default
现在,再来运行和刚才相同的测试代码,就能产生符合预期的结果了。
('Foo', {'stuff': 'why'})
('Bar', {'meep': 'i dunno'})
参数的默认值,只会在程序加载模块并读到本函数的定义时评估一次。对于 {} 或 [ ] 等动态的值,这可能会导致奇怪的行为。
对于以动态值作为实际默认值的关键字参数来说,应该把形式上的默认值写为None,并在函数的文档字符串里面描述该默认值所对应的实际行为。