0

重複の可能性:
Pythonの「驚き最小の原則」:可変のデフォルト引数

Python 2.7を使用していると、奇妙な動作に遭遇しました。それを説明する方法や、Pythonドキュメントに存在するかどうかさえわかりません。コードを使用する

class MyClass:
@staticmethod
def func(objects=[], a=None, b=None):
    objects.append(a)
    print 'objects: %s'%objects
    print 'b: %s'%b


MyClass.func(a='one')
MyClass.func(a='two', b='foo')
MyClass.func(a='three')

get出力を取得します

objects: ['one']
b: None
objects: ['one', 'two']
b: foo
objects: ['one', 'two', 'three']
b: None

ご覧のとおり、メソッドの最初のリストパラメータ(オブジェクト)は、呼び出し間でその値を保持します。ヘッダー宣言にデフォルト値[]がある場合でも、最後のリストに新しい値が追加されます。ただし、最後のパラメーター(b)はその値を保持せず、呼び出しの間にデフォルト値にリセットされます。

(とにかく私にとって)期待されるのは、メソッドの呼び出しでobjectsパラメーターをデフォルトにリセットする必要があることです(bパラメーターのように)が、これは発生しないようで、最初の呼び出しでのみ発生するようです。

誰かがこの行動を説明できますか?これはこのバージョンのPythonのバグですか、それとも意図された動作ですか?おそらく、リスト参照が呼び出し間で保持されていることと関係がありますが、文字列変数(b)はそうではありませんか?私はこの振る舞いに非常に混乱しています。

ありがとう

4

2 に答える 2

3

staticmethodであることとは何の関係もありません。これはPythonでよくある間違いです。

Pythonの関数は、単なるコードブロックではなく、ファーストクラスのオブジェクトであるため、パラメーターでオブジェクトに割り当てる空のリストは、関数オブジェクトに添付された実際のリストです。それを呼び出してそのリストに何かを追加するたびに、同じリストを使用しています。それがこのように起こっているのを見るのは簡単です:

>>> def func(x, objects=[]):
...     objects.append(x)
... 
>>> func(1)
>>> func.func_defaults
([1],)
>>> func(2)
>>> func.func_defaults
([1, 2],)
>>> func(3)
>>> func.func_defaults
([1, 2, 3],)

func_defaultsは、設定したデフォルトのキーワードパラメータを含む関数オブジェクト属性です。リストがどのようにあるかを見て、変更されますか?

あなたが望むことをする適切な方法は次のとおりです。

class MyClass:
@staticmethod
def func(objects=None, a=None, b=None):
    if objects is None:
        objects = []
    objects.append(a)
    print 'objects: %s'%objects
    print 'b: %s'%b
于 2012-04-20T00:58:29.950 に答える
1

この動作は広く知られており、多くの人が機能として扱っており、staticmethod固有ではありません。すべての機能で機能します。これは、引数にデフォルト値として可変オブジェクトを割り当てるときに発生します

StackOverflowでこの回答を参照してください:デフォルトの引数の落とし穴/可変のデフォルトの引数の危険性

可変性と不変性の問題を理解している場合は、次のような関数/メソッドを考えてください。

  • 定義されると、デフォルトの引数値が割り当てられます。
  • 呼び出されると、本体が実行され、変更可能なデフォルトの引数値がインプレースで変更されると、変更された値は、デフォルトの引数値が代わりに別の値を提供することによってオーバーライドされていない後続のすべての呼び出しで使用されます。
于 2012-04-20T01:03:29.663 に答える