われがわログ

最適化アルゴリズムとかプログラミングについて書きたい

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の方が意図がわかりやすいので良いのではと思う。

変数にアクセスするタイミングを意識すべきであった。

参考

docs.python.org

stackoverflow.com

余談

C++だとこんな感じにかける。std::bindではなくlambda式を使うのが推奨されているので、 PythonC++を行き来するときは要注意。

#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));