ラムダ関数をディクショナリに格納するときに、奇妙な動作が発生しました。ループ内の関数にデフォルト値を渡そうとすると、最後のデフォルト値のみが使用されます。
ここにいくつかの最小限の例があります:
#!/usr/bin/env python
# coding: utf-8
def myfct(one_value, another_value):
"do something with two int values"
return one_value + another_value
fct_dict = {'add_{}'.format(number): (lambda x: myfct(x, number))
for number in range(10)}
print('add_3(1): {}, id={}'.format(fct_dict['add_3'](1), id(fct_dict['add_3'])))
print('add_5(1): {}, id={}'.format(fct_dict['add_5'](1), id(fct_dict['add_5'])))
print('add_9(1): {}, id={}'.format(fct_dict['add_9'](1), id(fct_dict['add_9'])))
出力は次のようになります
add_3(1): 10, id=140421083875280
add_5(1): 10, id=140421083875520
add_9(1): 10, id=140421083876000
異なる関数(idは同一ではありません)を取得しますが、すべての関数は同じ2番目の引数を使用します。
誰かが何が起こっているのか説明できますか?
同じことがpython2、python3、pypyにも当てはまります...
修正:
def make_closure(number):
return lambda x: myfct(x, number)
使用されます
{'add_{}'.format(number): make_closure(number) for number in range(10)}
この動作の理由は、変数number
(ここでは名前付きメモリの場所)がループのすべての反復で同じであるためです(ただし、実際の値は反復ごとに変化します)。ここでの「ループ」とは、内部的にループに基づいている辞書理解を指します。lambda
ループで作成されたすべてのインスタンスは、同じ「場所」で閉じられ、最後に割り当てられた値が保持されます(ループの最後の反復で)。
次のコードは、実際にその下で発生するものではありません。これは、概念に光を当てるために提供されているだけです。
# Think of a closure variable (like number) as being an instance
# of the following class
class Cell:
def __init__(self, init=None):
self.value = None
# Pretend, the compiler "desugars" the dictionary comprehension into
# something like this:
hidden_result_dict = {}
hidden_cell_number = Cell()
for number in range(10):
hidden_cell_number.value = number
hidden_result_dictionary['add_{}'.format(number)] = create_lambda_closure(hidden_cell_number)
操作lambda
によって作成されたすべてのクロージャcreate_lambda_closure
はまったく同じCell
インスタンスを共有し、value
実行時(つまり、クロージャが実際に呼び出されたとき)に属性を取得します。その時までに、value
これまでに割り当てられた最後の値を参照します。
の値はhidden_result_dict
、dictの理解の結果として答えられます。(繰り返しますが、これは「概念」レベルで読み取ることのみを目的としています。PythonVMによって実行される実際のコードとは関係ありません)。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加