Python实验室:千万别把函数默认值设为,这个错误你犯过吗?

前言

在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

<class ‘tuple’>

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

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

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

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

运行结果:

[5]

[5]

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

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

运行结果:

2330464706440

[]

[]

[5]

[5]

2330464706440

2330464706440

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

总结

  • 我们在编码时千万不要以 ‘[]’ 作为函数参数的默认值,除非你非常清楚这里发生的情况是你所期望的。
  • 函数的默认值会以引用的形式缓存于函数的__defaults__属性中。
  • 如果您觉得此文对您有帮助,请转发给更多的人,并点击右上角「关注」按钮,了解更多博文更新。

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

    上一篇 2019年6月25日
    下一篇 2019年6月25日

    相关推荐