python:(lambda)函数的dict

Hen*_*ing 5 python lambda

将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执行的实际代码无关).