事象
Pythonで、ある関数のパラメータの値が一部異なる関数のリストを作りたい。
こんな感じ。
f = lambda a, x: a * x
f_list = [
lambda x: f(0, x),
lambda x: f(1, x),
lambda x: f(2, x)
]
で、そのまま次のように書いてみる。
f = lambda a, x: a * x
f_list = [lambda x: f(a, x) for a in range(3)]
すると、 f_list
の要素がすべて lambda x: f(2, x)
になってしまった。
2行目の a
の部分は0, 1, 2と代入されたものにしたい。
検証
なぜ同じ関数になってしまうのか確かめるために、 f
を関数で書き直し、オブジェクトのidをprintしてみる。
def f(a, x):
print(id(a))
return a * x
f_list = [lambda x: f(a, x) for a in range(3)]
f_list[0](1)
f_list[1](1)
f_list[2](1)
この結果は
id(a) = 140735854982000
id(a) = 140735854982000
id(a) = 140735854982000
a
のidはすべて同じ、つまり、同じオブジェクトということ。
ラムダ式の中でのパラメータ a
は、同じ参照になってしまうようである。
対策
同じ参照にならないように、 functools
の partial
を用いる。
from functools import partial
def f(a, x):
print('id(a) = ' + str(id(a)))
return a * x
f_list = [partial(f, a) for a in range(3)]
f_list[0](1)
f_list[1](1)
f_list[2](1)
この結果は
id(a) = 140735854981936
id(a) = 140735854981968
id(a) = 140735854982000
異なるidになった。
これは、もちろん f
をラムダ式に戻しても問題なし。
f = lambda a, x: a * x
f_list = [partial(f, a) for a in range(3)]
まとめ
関数を複製したい場合は、 functools.partial
を用いればよい。