Pythonでループ内でlambdaを使う際には注意しろ
Pythonでlambdaで関数オブジェクトを作り、リストにループで突っ込んだところ、 意図した結果にならず盛大に時間を溶かしたのでメモしておく。
まとめ
C++の std::bind
みたいにPythonのlambdaを使う場合には functools.partial
を使う。
本文
以下のコードの出力は何だろうか?
lambdas = [lambda : i for i in range(3)] for f in lambdas: print(f())
私は
1 2 3
となることを予想して上記プログラムを書いた。 だが、正解は
2 2 2
である。
これは、lambdaがi
にアクセスするタイミングが実行時であることに起因する。
0,1,2と出力させたい場合には、以下のように書く必要がある。
from functools import partial lambdas = [partial(lambda i: i, i) for i in range(3)] for f in lambdas: print(f())
partialを使わずに
lambdas = [lambda x=i: x for i in range(3)]
でもよいが、paritalの方が意図がわかりやすいので良いのではと思う。
変数にアクセスするタイミングを意識すべきであった。
参考
余談
C++だとこんな感じにかける。std::bind
ではなくlambda式を使うのが推奨されているので、
PythonとC++を行き来するときは要注意。
#include <iostream> #include <vector> #include <functional> int main() { auto lambdas = std::vector<std::function<int()>>{}; for (int i=0;i<3;i++){ lambdas.push_back([i]{ return i; }); } for (int i=0;i<3;i++){ std::cout << lambdas[i]() << std::endl; } }
std::bind
を使うとこう。
int nop(int x){ return x } ---- lambdas.push_back(std::bind(nop, i));