十分長い間 Python をいじっている人は誰でも、次の問題に悩まされています (またはバラバラに引き裂かれています)。
def foo(a=[]):
a.append(5)
return a
Python の初心者は、この関数が常に 1 つの要素のみを含むリストを返すことを期待するでしょう: [5]
. その代わりに、結果は非常に異なっており、非常に驚くべきものです (初心者にとって):
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
私のマネージャーはかつてこの機能に初めて遭遇し、言語の「劇的な設計上の欠陥」と呼んでいました。私は、この動作には根本的な説明があると答えました。内部構造を理解していないと、非常に不可解で予想外です。ただし、次の質問に (自分自身で) 答えることができませんでした: 関数の実行時ではなく、関数の定義時に既定の引数をバインドする理由は何ですか? 経験豊富な動作が実際に使用できるとは思えません (バグを繁殖させることなく、C で静的変数を実際に使用したのは誰ですか?)
編集:
Baczek は興味深い例を示しました。あなたのコメントのほとんどと特にウタールのコメントとともに、私はさらに詳しく説明しました。
>>> def a():
... print("a executed")
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print(x)
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
私には、設計上の決定は、パラメーターのスコープをどこに配置するかに関連しているように見えます: 関数内、またはそれと「一緒に」?
関数内でバインドを行うx
と、関数が呼び出されたときに指定されたデフォルトに効果的にバインドされ、定義されていないことを意味します。これは、深刻な欠陥をもたらすものです:def
行は、バインドの一部 (の関数オブジェクト) は定義時に発生し、一部 (デフォルト パラメーターの割り当て) は関数呼び出し時に発生します。
実際の動作はより一貫しています。その行が実行されるとき、つまり関数定義時に、その行のすべてが評価されます。