Python に限らないと思いますが、知らないとハマることになるやつですね。
例えば、以下のように関数を登録して後から実行した場合、
def hoge(x):
print(x)
funcs = []
for i in range(5):
funcs.append(lambda: hoge(i))
for func in funcs:
func()
期待する動作は 0~4 が順に出力されることですが、上記だと全て 4
が出力されます。
ここのラムダ式は遅延評価、関数が呼ばれたときに初めて評価されます。
従って、関数が実行されたときは既にループが終わった後であり、その時には i
は 4
になっているため、常に 4
と出力されてしまいます。
解決方法として、私が知っている範囲では以下の2パターンがあります。
デフォルト引数で対応
以下のようにラムダ式にデフォルト引数を与えてやります。
def hoge(x):
print(x)
funcs = []
for i in range(5):
funcs.append(lambda x=i: hoge(x))
for func in funcs:
func()
x=i
とすることで即時評価され、期待した出力となります。
しかし、以下のように実行時に引数を与えると、与えられた引数で上書きされてしまいます。
def hoge(x):
print(x)
funcs = []
for i in range(5):
funcs.append(lambda x=i: hoge(x))
for func in funcs:
func(4)
次の方法だとこの問題も回避できます。
functools.partial を使う
functools.partial()
は 引数を部分的に適用(固定化?)した関数を生成する関数です。
以下のように書くことで期待した出力となります。
from functools import partial
def hoge(x):
print(x)
funcs = []
for i in range(5):
funcs.append(partial(hoge, i))
for func in funcs:
func()
この場合、実行時に引数を与えるとエラーになります。