Default Parameter Values in Python

28 Apr 2016 Author: GIGI WANG

翻译->原文在这里

Python中的默认参数值

Python中默认参数值的处理是一些容易绊倒大多数Python新手程序员的事情之一(但通常只会有一次)。 造成这种困惑的原因是当使用一个可变的对象作为默认值,也就是在某些地方可以被修改的一个值,比如一个list或一个字典。 一个例子:

>>> def function(data=[]):
...     data.append(1)
...     return data
...
>>> function()
[1]
>>> function()
[1, 1]
>>> function()
[1, 1, 1]

正如你看到的,这个list变得越来越长。如果查看这个list的identity,会发现函数原来一直返回同一对象:

>>> id(function())
12516768
>>> id(function())
12516768
>>> id(function())
12516768

这个原因很简单:函数在每次调用中一直使用同一的对象。我们做的修改有“粘性”。 这是什么鬼?# 当其所属的”def”语句被执行,默认参数值就会确定,参看: http://docs.python.org/ref/function.html (挂了) Python语言参考相应内容 同样注意到“def”是python中一个可执行语句,而且默认参数在“def”语句的环境中被赋值.如果多次执行”def”语句,它将每次创建一个新的函数对象(包括重新计算默认值)。 我们来看下面的例子。 该怎么做呢?# 解决方法是这样的,如别人提过的,使用一个占位符,而不是修改默认值。None 就是一个普通值:

def myfunc(value=None):
    if value is None:
        value = []
    # modify value here

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

sentinel = object()
def myfunc(value=sentinel):
    if value is sentinel:
        value = expression
    # use/modify value here

在稍旧的代码中,在”object”采用之前,有时可以看到像这样语句 sentinel = ['placeholder']被用来创建一个拥有唯一标识的Non-false对象;[]每次赋值时都创建一个新的list。 正确使用可变默认值# 最后,应该注意到,更多的高级Python代码常常运用这一机制的优势;例如,当在一个循环中创建一批UI按钮,可以尝试这样:

for i in range(10):
    def callback():
        print "clicked button", i
    UI.Button("button %s" % i, callback)

所有的callback函数打印相同值(这种情况下很可能是9).原因是Python嵌套作用域绑定变量,而不是对象值,因此所有的callback实例都会显示变量“i”的当前值。 使用explicit绑定来修复这一问题:

for i in range(10):
    def callback(i=i):
        print "clicked button", i
    UI.Button("button %s" % i, callback)

“i=i” 绑定参数“i”(本地变量)到当前外部变量“i”的当前值。 两种其它使用:本地缓存/记忆使用;例如

def calculate(a, b, c, memo={}):
    try:
        value = memo[a, b, c] # return already calculated value
    except KeyError:
        value = heavy_calculation(a, b, c)
        memo[a, b, c] = value # update the memo dictionary
    return value

(这对于某些特定的递归算法特别好) 而且,为了更加优化的代码,使用全局名称的本地重绑定:

import math
def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
    ...

细节上是怎么处理的呢? 当Pyhton执行“def”语句,它会产生一些就绪的片(包括已编译的函数体代码和当前命名空间),创建一个新的函数对象,完成后默认值也将确定。 一系列不同的可用组件作为函数对象的属性,上面用到的函数:

>>> function.func_name
'function'
>>> function.func_code
<code object function at 00BEC770, file "\<stdin\>", line 1>
>>> function.func_defaults
([1, 1, 1],)
>>> function.func_globals
{'function': \<function function at 0x00BF1C30\>,
'__builtins__': \<module '__builtin__' (built-in)\>,
'__name__': '__main__', '__doc__': None}

因为你可以访问默认参数,可以修改它们:

>>> function.func_defaults[0][:] = []
>>> function()
[1]
>>> function.func_defaults
([1],)

然而,这不是我想要推荐的常规使用方法. 另外一种重置默认方法是 简单地重新执行同一”def”语句,Python 创建一个新的代码对象绑定,确定默认值,像之前一样分配函数对象到相同的变量。 你需要明确你在做什么然后再这样做。 是的,如果碰巧不是这个函数,可以使用函数类的新模块中创建自己的函数对象。