关于python:可变函数参数默认值的良好用途?

Good uses for mutable function argument default values?

在Python中,将可变对象设置为函数中参数的默认值是一个常见的错误。下面是大卫·古德这篇精彩的文章中的一个例子:

1
2
3
4
5
6
7
>>> def bad_append(new_item, a_list=[]):
        a_list.append(new_item)
        return a_list
>>> print bad_append('one')
['one']
>>> print bad_append('two')
['one', 'two']

这就是为什么会发生这种情况的原因。

现在我的问题是:这个语法有一个好的用例吗?

我的意思是,如果每个遇到它的人都犯同样的错误,揭穿它,理解问题,并从中试图避免它,那么这种语法有什么用呢?


您可以使用它在函数调用之间缓存值:

1
2
3
4
def get_from_cache(name, cache={}):
    if name in cache: return cache[name]
    cache[name] = result = expensive_calculation()
    return result

但是通常这类事情在类中做得更好,因为您可以拥有其他属性来清除缓存等。


1
2
3
4
import random

def ten_random_numbers(rng=random):
    return [rng.random() for i in xrange(10)]

使用random模块作为默认的随机数生成器,它实际上是一个可变的单例。


规范答案是:http://effbot.org/zone/default-values.htm

它还提到了可变默认参数的3个"好"用例:

  • 将局部变量绑定到回调中外部变量的当前值
  • 缓存/内存化
  • 全局名称的本地重新绑定(用于高度优化的代码)

也许您不改变可变参数,但需要可变参数:

1
2
3
4
def foo(x, y, config={}):
    my_config = {'debug': True, 'verbose': False}
    my_config.update(config)
    return bar(x, my_config) + baz(y, my_config)

(是的,我知道在这种情况下,你可以使用config=(),但我发现这不太清楚,也不太一般。)


编辑(澄清):可变默认参数问题是更深层次设计选择的一个症状,即默认参数值存储为函数对象上的属性。你可能会问为什么要做出这样的选择;像往常一样,这些问题很难正确回答。但它确实有很好的用途:

优化性能:

1
def foo(sin=math.sin): ...

在闭包中而不是变量中获取对象值。

1
2
3
4
callbacks = []
for i in range(10):
    def callback(i=i): ...
    callbacks.append(callback)