web-dev-qa-db-ja.com

可変関数の引数のデフォルト値の良い使い方は?

Python=で、関数の引数のデフォルト値として可変オブジェクトを設定するのはよくある間違いです。これは から取られた例です。デビッド・グッドガー

>>> 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']

これが発生する理由は、 こちら です。

そして今私の質問のために:この構文の良いユースケースはありますか?

つまり、それに遭遇したすべての人が同じ間違いを犯し、それをデバッグし、問題を理解し、そこからそれを回避しようとすると、そのような構文にはどのような用途がありますか?

66
Jonathan

これを使用して、関数呼び出し間の値をキャッシュできます。

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

しかし、通常、そのようなことは、キャッシュをクリアするための追加の属性を持つことができるので、クラスを使用することでよりうまく行われます。

38
Duncan
import random

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

randomモジュールを使用して、事実上可変のシングルトンをデフォルトの乱数ジェネレーターとして使用します。

6
Fred Foo

多分あなたは変更可能な引数を変更しませんが、変更可能な引数を期待します:

_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=()を使用できることを知っていますが、あまり明確でなく、一般的ではないことがわかります。)

5
WolframH

標準的な答えはこのページです: http://effbot.org/zone/default-values.htm

また、可変デフォルト引数の3つの「良い」使用例についても触れています。

  • コールバックで外部変数の現在の値にローカル変数をバインドする
  • キャッシュ/メモ化
  • グローバル名のローカル再バインド(高度に最適化されたコードの場合)
4
Peter M.

編集(説明):変更可能なデフォルト引数の問題は、より深い設計の選択の症状です。つまり、デフォルト引数の値は、関数オブジェクトの属性として格納されます。この選択が行われた理由を尋ねるかもしれません。いつものように、そのような質問は適切に答えることが困難です。しかし、それは確かに良い用途を持っています:

パフォーマンスの最適化:

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

変数の代わりにクロージャーでオブジェクト値を取得します。

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