在python中,可以把函数中一开始不能提前固定的值,当做关键字参数的默认值。
例如,记录日志消息时,默认的时间应该是触发的那一刻。所以如果函数调用者没有指定时间,则就使用触发的那一刻时间。
看下面这种写法
from time import sleep
import datetime
def log(msg, when = datetime.datetime.now()):
print(f"{when}: {msg}")
log("test1")
sleep(0.5)
log("test2")
# 2022-01-18 00:01:25.427478: test1
# 2022-01-18 00:01:25.427478: test2
这样写不行,因为datetime.datetime.now()只执行了一次,所以每条日志的这个默认值相同。参数的默认值只会在加载这个模块的时候,计算一遍,而不是每次执行重新计算。
要实现这个效果,惯用的办法是吧参数默认值设为None,同时在函数说明(docstring)里写清楚,这个参数为None时,函数怎么运行。
改造如下:
from time import sleep
import datetime
def log(msg, when=None):
if when is None:
when = datetime.datetime.now()
print(f"{when}: {msg}")
log("test1")
sleep(0.5)
log("test2")
# 2022-01-18 00:06:05.449128: test1
# 2022-01-18 00:06:05.964637: test2
这样执行结果就不同了。
把参数的默认值设为None还有个意义,就是用来表示那种以后可能有调用者修改内容的默认值。如,写一个函数对采用JSON格式编码的数据做解码,如果无法解码,就返回指定的默认值,如果调用者没有指定,就返回空白的字典。
如下:
import json
def decode(data, default={}):
try:
return json.loads(data)
except ValueError:
return default
data1 = decode("no json data")
data1['stuff'] = 5
data2 = decode("no json data too")
data2['meep'] = 1
print(data1) # {'stuff': 5, 'meep': 1}
print(data2) # {'stuff': 5, 'meep': 1}
print(id(data1)) # 2610742776064
print(id(data2)) # 2610742776064
本来目的是让两次调用得到两个不同的空白字典,然后每个字典存各自的值,但是可以看到,其实两个是同一个字典。这是因为系统一开始给default参数确定默认值时,就分配好了那个空字典。
下面也使用None解决这个问题:
import json
def decode(data, default=None):
try:
return json.loads(data)
except ValueError:
if default is None:
default = {}
return default
data1 = decode("no json data")
data1['stuff'] = 5
data2 = decode("no json data too")
data2['meep'] = 1
print(data1) # {'stuff': 5}
print(data2) # {'meep': 1}
print(id(data1)) # 2361700135680
print(id(data2)) # 2361701174656
这样就得到了预期结果。