Python中的默认参数值

来源:互联网 发布:微博怎么设置域名 编辑:程序博客网 时间:2024/06/07 08:42

Python对默认参数值的处理方法是少有的几个易使大多数新手Python程序员犯错的地方之一。(通常只犯一次)

导致困惑的地方是当你使用“可变”对象作为(参数的)默认值时的(程序)行为。(可变)也就是说值可以原地修改,像列表或字典。

看下面的例子:

Python中的默认参数值

就像你看到的那样,列表变得越来越长。如果你查看列表的ID,你会发现函数实际上总是返回同一个对象。

原因很简单:函数在每次调用时总是使用同一个对象。我们做的修改行为有“粘性”。

为什么会这样?

属于函数定义的默认参数值当且仅当“def”语句执行时求值。请看:

语言参考中的相关章节。

要注意Python中的“def”语句是可执行的,而且默认参数是在“def”语句的环境下求值的。如果“def”语句执行了多次,那么它每次将产生新的函数对象(对象会带着全新的默认值)。下面我们会看到这样的例子的。

那要怎么做?

迂回方法是,就像其他人已经提到了的,使用占位符值来替代默认值。None是一个常用的值:

如果你需要处理任意对象(包括None),你可以使用哨兵对象:

在很早以前,即在“object”对象引入之前,有时你会看到像下面这样的代码:

这个代码用于创建一个具有唯一ID的对象;这对中括号([])在每次执行时产生新的列表。

对可变默认参数的合法(合理)使用

最后,要提到很多高级的Python代码经常使用这个机制的好处。例如,假设你要在一个循环里创建了一堆UI按钮,而你可能会使用像下面这样的代码:

这样你会发现所有的回调函数都打印出相同的值(在这个情况下,很可能是9)。原因是Python的嵌套作用域是绑定到变量的,而不是绑定到对象值的。所以所有的回调函数实例将会看到当前(也是最后)的变量“i”的值。为了修正这个问题,使用下面的代码:

那个“i=i”的步伐将绑定参数“i”(一个局部变量)到当前的外部变量“i”的值。

两个其他的例子使用的是局部缓存:

(这对某些递归算法很好)

另一个例子,对应高度优化的代码,对全局名字的局部重新绑定:

详细来说,这是怎么工作的?

当Python执行“def”语句时,它使用一些已有的东西(包括编译了的函数体的代码和当前的名字空间),然后创建出一个新的函数对象。当它做这个的时候,默认值也会被求值。

这些各种各样的组件也能作为函数对象的属性而访问。使用我们先前定义过的函数:

Python中的默认参数值

由于你可以访问它们,因而你也可以修改它们:

当然,这可不是我推荐的正常使用方法。

另一个重置默认值的方法就是简单的重新执行一下那个相同的“def”语句。Python会重新创建一个新的到这个代码对象绑定,重新计算默认值,然后将这个函数对象赋值给同以前一样的那个变量。但是要着重指出,请在你确切知道你在做什么的时候才能这么做。

最后,如果你刚好有函数各个部分,而不是函数本身,你可以使用new模块中的function类来创建你自己的函数对象。

0 0
原创粉丝点击