Bootstrap

什么是默认值?什么时候需设置默认值?_Python实验室:千万别把函数默认值设为「」,这个错误你犯过吗?...

前言

在Python的函数定义中,我们可以给函数形参指定一个默认值,这样,在函数使用时,如果默认值已满足我们的需求,我们就可以略过该参数;如果不满足,我们也可以指定其他值,这样可以使设计的函数兼顾易用性与灵活性。但在Python中有一点需要注意,千万不要以 '[]' 作为参数默认值。

f304792b40084615c5da7b6222785488.png

探索

为什么不能将 '[]' 作为函数默认值呢?我们从一个简单的例子开始:

48f6839a4a349f105603c0e872cf6230.png

运行结果:

[5]

[5, 5]

[5, 5, 5]

三次调用func(),期望的结果是每次都返回一个[5],但实际上返回的列表中5的个数逐次增多。这样的结果表明,似乎有个列表被缓存在函数中,每次调用的时候都会对这个列表进行操作。

我们在调用中间插入一些指定参数值的调用,看看结果如何:

051cba7a9fc8742ae69a72b2757482ca.png

运行结果:

[5]

[5, 5]

[5, 5, 5]

[5]

[5, 5, 5, 5]

从结果看出,对于指定参数值的调用,返回的结果与期望的一致,但是在再次使用默认值时,又出现同样的情况。

我们进一步分析返回的结果:

2fa18c0808b5ddaf042b03c43375a630.png

运行结果:

2929752624712

2929752624712

2929752624712

2929752567624

2929752624712

从以上结果可以看出,对于所有使用默认值的情形,操作和返回的都是同一个列表,并没有每次调用函数时创建一个新列表作为默认值,这也就可以解释为什么返回列表中的值越来越多。

那这个神秘的列表缓存到哪了呢?

92413520dec743716d8fc9ed85f1098e.png

运行结果:

([],)

([5],)

([5, 5],)

1863704923720

1863704923720

从以上结果可以看出,作为默认值的列表实际上被缓存在函数func的属性__default__中,__default__是一个元组,会缓存所有函数参数的默认值。

那默认值列表是什么时候创建出来的呢?

参数默认值实际上是Python解析器第一次解析语句 def func(input = [])的时候创建的,这个过程实际上会创建一个函数对象,同时设置函数对象的__default__属性,以后每次以默认值调用函数时就修改__default__属性中缓存的值。

'[]' 既然不应该作为函数的默认值,那我们应该如何修改函数实现以达到同样的目标呢?参考如下实现:

a7ba80ec7f12ffb1d0e0f610b602556f.png

运行结果:

[5]

[5]

推荐的做法是:以None作为默认参数,在函数体内对输入参数进行检查,在input为None时创建空列表并使用。

对于面向对象编程,还有一个类似但比较隐晦的例子:

3ed9221716ca3547b93f92587d617321.png

运行结果:

2330464706440

[]

[]

[5]

[5]

2330464706440

2330464706440

当我们以一个空列表作为类的__init__函数的形参默认值时,如果在创建对象时使用默认参数值,那么这个列表将在所有以默认值创建的对象间共享。另外,第一行的结果实际表明,即使在类还没有任何对象的时候,函数的默认值列表已经被创建并被缓存在函数__init__中,这个过程与函数默认值一致。

总结

  • 我们在编码时千万不要以 '[]' 作为函数参数的默认值,除非你非常清楚这里发生的情况是你所期望的。
  • 函数的默认值会以引用的形式缓存于函数的__defaults__属性中。
  • 本文虽以 '[]' 贯穿全文,但实际上对于可变变量都存在同样的问题,读者需要留心。

如果您觉得此文对您有帮助,请转发给更多的人,并点击右上角「关注」按钮,了解更多博文更新。

;