使用None和文本字符串来指定动态默认参数(Use None and Docstrings to Specify Dynamic Default Arguments)。
有些时候可能需要使用非静态类型作为关键字参数的默认值,比如你想在打印日志的时候同时输出日志事件发生的时间。默认情况下,你想记录方法被调用的时间,没准你就直接写下了下面的代码,你可能会认为每次调用的时候都会重新更新默认参数值:
![9c1e8aeb227aa3b6fda0cff4972da281.png](https://i-blog.csdnimg.cn/blog_migrate/6a7e1177c1155070211716f3450dae42.jpeg)
但是实际上,所有输出的时间都是一样的,这个时间就是方法被定义时的时间。参数的默认值只会在模块被加载时执行一次,这通常会发生在程序启动的时候。一旦包含这类方法的代码被加载了,所有像datetime.now的参数就不会再被计算了。
在Python中想要实现这种功能的惯例是给默认参数赋值为None,同时在docstring中说明如何处理这种None值。而在你的方法体中,一旦发现默认参数值是None就为它赋予期望值。据此修改上面代码如下:
![33c5ab712de9fa390db1d7cc0dc75dcf.png](https://i-blog.csdnimg.cn/blog_migrate/022ce03ed758e68782c2d1d763821bf1.jpeg)
此时输出的时间戳就已经是正确的了:
![6e23579b99ad73aa75c5c4b4df3ae057.png](https://i-blog.csdnimg.cn/blog_migrate/944dadaa19531fa4ff6a8ae19c7bbc00.jpeg)
使用None作为那些经常发生变化的参数的默认值非常重要。例如,你写了一个方法用来加载JSON文件,如果加载失败你希望返回一个空字典,代码可以写成下面的样子:
![43811112bbf5e3ce2695b654cb183a41.png](https://i-blog.csdnimg.cn/blog_migrate/4f97836280b90ad0c1d75e2e319e0e66.jpeg)
此时出现的问题就像前面提到的datetime.now一样,所有调用这个方法失败是返回的空字典都是一个,也就是说它们的返回值是共享的。这就会产生很多奇怪的问题:
![8a8692c7c36391bb358af2fd852d474f.png](https://i-blog.csdnimg.cn/blog_migrate/6616a58118e38205f06e8f8fcce53e15.jpeg)
本来你希望这两次方法调用能产生两个不同的返回值,每一个返回值都有自己的key和value。但是实际上,对一个返回值的修改也会修改另一个返回值。出现这种错误的罪魁祸首就是返回值Foo和Bar其实都是参数的默认值,即同一个字典对象。
![287de3e0e2f7a9c47f7cfc8f34ef2cd4.png](https://i-blog.csdnimg.cn/blog_migrate/9853edfa21beedb535cb37b744c0cc0a.jpeg)
解决办法就是将参数的默认值设置成None,同时在docstring中详细写明方法提如何处理默认参数的:
![6e836bddb5cb0bf064745b29a2d32005.png](https://i-blog.csdnimg.cn/blog_migrate/ecbf656849ecc98584e28ea994cb9729.jpeg)
此时再去调用这个方法时就不会出现任何异常了:
![19afb3e715cbc3ea46f6b60a1aa670c1.png](https://i-blog.csdnimg.cn/blog_migrate/1b1135d4c3ec30d4fbe396a37959e262.jpeg)
总结
- 默认参数只会被赋值一次:在方法声明时所在的模块被加载时。这回产生很多奇怪的问题(例如:{}和[]).
- 对于那些拥有动态参数值的参数,使用None作为默认值,在方法的文档说明中详细介绍参数默认值的行为。