はんぎょねこの憂鬱

耳から変な汁が出てきた

Python lambda式の落とし穴

Pythonクロージャ内部で使われている変数は通常、実行時に値が評価されます。for文などと組み合わせてlambda式やローカル関数を使うときは、ちょっと気を付けないといけません。

例えば、0~4まで出力する関数を5つ用意したいとき

# 関数作る
functions = []
for i in range(0, 5):
    functions.append(lambda: print(i))

# 関数呼び出す
for func in functions:
    func() # この時点では i は 4 ですよ?

このように書いてしまうと、下記のような結果が出力されてしまいます。

4
4
4
4
4

こいつを回避するためには、デフォルト引数を使ってちょいと工夫してやる必要があります。

# 関数作る
functions = []
for i in range(0, 5):
    functions.append(lambda x=i: print(x)) # デフォルト引数は関数定義時に評価される

# 関数呼び出す
for func in functions:
    func()

これで意図した結果が得られます。

0
1
2
3
4

functools.partialを使う方法もあるようですね。

参照:Guide to Python - Common Gotchas