将lambda函数存储到字典中时,我遇到了一些奇怪的行为:如果尝试将某个默认值传递给循环中的函数,则只使用最后一个默认值.
这是一些最小的例子:
#!/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'])))
Run Code Online (Sandbox Code Playgroud)
输出如下
add_3(1): 10, id=140421083875280
add_5(1): 10, id=140421083875520
add_9(1): 10, id=140421083876000
Run Code Online (Sandbox Code Playgroud)
你得到不同的函数(id不相同),但每个函数使用相同的第二个参数.
有人能解释一下发生了什么吗?
python2,python3,pypy也是如此......
Dir*_*irk 10
修复:
def make_closure(number):
return lambda x: myfct(x, number)
Run Code Online (Sandbox Code Playgroud)
用作
{'add_{}'.format(number): make_closure(number) for number in range(10)}
Run Code Online (Sandbox Code Playgroud)
这种行为的原因是,变量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)
Run Code Online (Sandbox Code Playgroud)
操作lambda创建的所有闭包create_lambda_closure共享同一个Cell实例,并value在运行时获取属性(即,实际调用闭包时).到那时,value将参考分配给它的最后一个值.
hidden_result_dict然后将该值作为词典理解的结果来回答.(同样:这仅仅意味着在"概念"级别上阅读;它与Python VM执行的实际代码无关).