前言
在Python的函数定义中,我们可以给函数形参指定一个默认值,这样,在函数使用时,如果默认值已满足我们的需求,我们就可以略过该参数;如果不满足,我们也可以指定其他值,这样可以使设计的函数兼顾易用性与灵活性。但在Python中有一点需要注意,千万不要以 '[]' 作为参数默认值。
探索
为什么不能将 '[]' 作为函数默认值呢?我们从一个简单的例子开始:
运行结果:
[5]
[5, 5]
[5, 5, 5]
三次调用func(),期望的结果是每次都返回一个[5],但实际上返回的列表中5的个数逐次增多。这样的结果表明,似乎有个列表被缓存在函数中,每次调用的时候都会对这个列表进行操作。
我们在调用中间插入一些指定参数值的调用,看看结果如何:
运行结果:
[5]
[5, 5]
[5, 5, 5]
[5]
[5, 5, 5, 5]
从结果看出,对于指定参数值的调用,返回的结果与期望的一致,但是在再次使用默认值时,又出现同样的情况。
我们进一步分析返回的结果:
运行结果:
2929752624712
2929752624712
2929752624712
2929752567624
2929752624712
从以上结果可以看出,对于所有使用默认值的情形,操作和返回的都是同一个列表,并没有每次调用函数时创建一个新列表作为默认值,这也就可以解释为什么返回列表中的值越来越多。
那这个神秘的列表缓存到哪了呢?
运行结果:
([],)
([5],)
([5, 5],)
1863704923720
1863704923720
从以上结果可以看出,作为默认值的列表实际上被缓存在函数func的属性__default__中,__default__是一个元组,会缓存所有函数参数的默认值。
那默认值列表是什么时候创建出来的呢?
参数默认值实际上是Python解析器第一次解析语句 def func(input = [])的时候创建的,这个过程实际上会创建一个函数对象,同时设置函数对象的__default__属性,以后每次以默认值调用函数时就修改__default__属性中缓存的值。
'[]' 既然不应该作为函数的默认值,那我们应该如何修改函数实现以达到同样的目标呢?参考如下实现:
运行结果:
[5]
[5]
推荐的做法是:以None作为默认参数,在函数体内对输入参数进行检查,在input为None时创建空列表并使用。
对于面向对象编程,还有一个类似但比较隐晦的例子:
运行结果:
2330464706440
[]
[]
[5]
[5]
2330464706440
2330464706440
当我们以一个空列表作为类的__init__函数的形参默认值时,如果在创建对象时使用默认参数值,那么这个列表将在所有以默认值创建的对象间共享。另外,第一行的结果实际表明,即使在类还没有任何对象的时候,函数的默认值列表已经被创建并被缓存在函数__init__中,这个过程与函数默认值一致。
总结
- 我们在编码时千万不要以 '[]' 作为函数参数的默认值,除非你非常清楚这里发生的情况是你所期望的。
- 函数的默认值会以引用的形式缓存于函数的__defaults__属性中。
- 本文虽以 '[]' 贯穿全文,但实际上对于可变变量都存在同样的问题,读者需要留心。
如果您觉得此文对您有帮助,请转发给更多的人,并点击右上角「关注」按钮,了解更多博文更新。