1. ホーム
  2. python

[解決済み] 最小限の驚き」と「変更可能なデフォルトの引数

2022-03-14 21:50:52

質問

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 の行は、(関数オブジェクトの)結合の一部が定義時に起こり、(デフォルトパラメータの割り当てが)関数の呼び出し時に起こるという意味で、quot;hybrid" になります。

実際の動作は、より一貫しています。その行のすべてが、その行が実行されたとき、つまり関数定義時に評価されます。

解決方法は?

実は、これは設計上の欠陥でもなければ、内部やパフォーマンスのせいでもないのです。
これは、Pythonの関数がファーストクラスのオブジェクトであり、単なるコードの一部ではないという事実から来ています。

関数は、その定義に基づいて評価されるオブジェクトです。デフォルトのパラメータは一種のメンバーデータであり、したがって、他のオブジェクトとまったく同様に、呼び出しごとにその状態が変化する可能性があります。

いずれにせよ、この動作の理由については、Effbotが非常に素晴らしい説明をしてくれています。 Pythonにおけるパラメータのデフォルト値 .
とても分かりやすいので、関数オブジェクトの仕組みを知るために読むことをお勧めします。