可变函数参数默认值的良好用途?

Jon*_*han 59 python arguments mutable default-value

在Python中将一个可变对象设置为函数中参数的默认值是一个常见的错误.以下是David Goodger撰写的优秀文章中的一个例子:

>>> 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']
Run Code Online (Sandbox Code Playgroud)

之所以出现这种情况的解释是在这里.

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

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

Dun*_*can 42

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

def get_from_cache(name, cache={}):
    if name in cache: return cache[name]
    cache[name] = result = expensive_calculation()
    return result
Run Code Online (Sandbox Code Playgroud)

但通常使用类可以做得更好,因为您可以使用其他属性来清除缓存等.

  • `@ functools.lru_cache(MAXSIZE =无)` (22认同)
  • ......或者是一个记忆装饰者. (9认同)
  • @Synedraacus:这个食谱也是如此。 (9认同)
  • @matrineau不一定。如果您的某些参数是可哈希的,而其他参数则不是,您可以使用此方法仅兑现可哈希的参数。`lru_cache` 要求*所有*参数都是可散列的。 (7认同)
  • 如果您有不可散列的值,则“lru_cache”不可用。 (4认同)
  • @katrielalex lru_cache是​​Python 3.2中的新功能,所以不是每个人都可以使用它. (3认同)
  • 仅供参考,现在有 backports.functools_lru_cache https://pypi.python.org/pypi/backports.functools_lru_cache (2认同)

Pet*_*ica 14

规范的答案是这个页面:http : //effbot.org/zone/default-values.htm

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

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

  • 看起来“将局部变量绑定到回调中外部变量的当前值”只是 Python 中另一个设计缺陷的解决方法。 (2认同)

sim*_*mon 8

我知道这是一个旧的,但只是为了它我想添加一个用例到这个线程。我定期为 TensorFlow/Keras 编写自定义函数和层,将脚本上传到服务器,在那里训练模型(使用自定义对象),然后保存模型并下载它们。为了加载这些模型,我需要提供一个包含所有这些自定义对象的字典。

在像我这样的情况下,您可以做的就是向包含这些自定义对象的模块添加一些代码:

custom_objects = {}

def custom_object(obj, storage=custom_objects):
    storage[obj.__name__] = obj
    return obj
Run Code Online (Sandbox Code Playgroud)

然后,我可以装饰字典中需要的任何类/函数

@custom_object
def some_function(x):
    return 3*x*x + 2*x - 2
Run Code Online (Sandbox Code Playgroud)

此外,假设我想将自定义损失函数存储在与自定义 Keras 层不同的字典中。使用 functools.partial 让我可以轻松访问新的装饰器

import functools
import tf

custom_losses = {}
custom_loss = functools.partial(custom_object, storage=custom_losses)

@custom_loss
def my_loss(y, y_pred):
    return tf.reduce_mean(tf.square(y - y_pred))
Run Code Online (Sandbox Code Playgroud)


Fre*_*Foo 6

import random

def ten_random_numbers(rng=random):
    return [rng.random() for i in xrange(10)]
Run Code Online (Sandbox Code Playgroud)

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

  • 但这也不是一个非常重要的用例. (6认同)
  • 我认为在Python的“一次获取引用”与Python的“每个函数调用一次查找”随机”之间,行为没有区别。两者最终都使用相同的对象。 (2认同)

Rei*_*ica 6

也许您不改变可变参数,但是期望可变参数:

def foo(x, y, config={}):
    my_config = {'debug': True, 'verbose': False}
    my_config.update(config)
    return bar(x, my_config) + baz(y, my_config)
Run Code Online (Sandbox Code Playgroud)

(是的,我知道您可以config=()在这种特殊情况下使用,但是我发现这不太清楚,也不太笼统。)

  • 还要确保您**不改变**并且**不直接从函数返回**这个默认值,否则函数外部的某些代码可以改变它并会影响所有函数调用。 (5认同)