在将 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'])))
输出如下
add_3(1): 10, id=140421083875280
add_5(1): 10, id=140421083875520
add_9(1): 10, id=140421083876000
您会得到不同的函数(id 不相同),但每个函数都使用相同的第二个参数。
有人可以解释一下发生了什么事吗?
python2、python3、pypy...也是如此。
The fix:
def make_closure(number):
return lambda x: myfct(x, number)
used as
{'add_{}'.format(number): make_closure(number) for number in range(10)}
出现此行为的原因是变量number
(想想:这里的命名内存位置)在循环的所有迭代期间都是相同的(尽管它的实际值在每次迭代中都会发生变化)。这里的“循环”指的是字典理解,其内部是基于循环的。全部lambda
循环中创建的实例将在同一“位置”上关闭,该位置保留最后分配给它的值(在循环的最后一次迭代中)。
下面的代码是not下面实际发生了什么。它只是为了阐明以下概念:
# 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)
All lambda
由创建的关闭create_lambda_closure
操作共享完全相同Cell
实例并会抓住value
属性在运行时(即,当闭包被实际调用时)。到那个时候,value
将引用最后分配给它的值。
的价值hidden_result_dict
然后作为字典理解的结果得到回答。 (再次强调:这仅意味着在“概念”级别上阅读;它与 Python VM 执行的实际代码无关)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)