同じ (変更可能な) オブジェクトへの参照と別のオブジェクトへの参照を混同することは、実際には "落とし穴" です (変更可能なオブジェクトと、もちろん参照を持つすべての非関数型言語が苦しんでいます)。初心者の Python コードでよく見られるバグは、変更可能なデフォルト値の誤用です。
def addone(item, alist=[]):
alist.append(item)
return alist
このコードは、データが C で機能するのとaddone
同じように、独自の状態を保持する (そして 1 つの増加するリストを連続する呼び出し元に返す) ことが目的である場合に正しい可能性があります。static
呼び出しごとに新しい空のリストが作成されるとコーダーが誤って想定している場合、それは正しくありません。
関数型言語に慣れた生の初心者は、Python の組み込みコンテナのコマンドとクエリの分離設計の決定によって混乱する可能性もあります。具体的に返すものがない変更メソッド (つまり、変更メソッドの大部分) は何も返さない (具体的には、彼らは戻ってきNone
ます)-彼らはすべての作業を「その場で」行っています。これを誤解して発生したバグは、簡単に見つけることができます。
alist = alist.append(item)
バグであることがほぼ保証されています. namealist
で参照されるリストにアイテムを追加しますが、 namealist
をNone
(呼び出しの戻り値append
) に再バインドします。
私が言及した最初の問題は、バインディングが遅いバインディングであると考える人々を誤解させる可能性のある早期バインディングに関するものですが、逆に問題が発生する可能性があります。 、代わりに、遅い。例(架空のGUIフレームワークを使用...):
for i in range(10):
Button(text="Button #%s" % i,
click=lambda: say("I'm #%s!" % i))
これは、「ボタン #0」、「ボタン #1」などの 10 個のボタンを表示しますが、クリックすると、すべてのボタンが表示されsay
ます。修正は、引数のデフォルト値が事前にバインドされているという事実を利用して(最初の問題について指摘したように!-)、最後の行を次のように変更することです。#9
i
lambda
click=lambda i=i: say("I'm #%s!" % i))
現在、 はデフォルト値をlambda
持つi
引数であり、もはや自由変数 (レキシカル クロージャによって検索される) ではないため、コードは意図したとおりに機能します (もちろん、他の方法もあります)。