我在Python混合理解列表和lambda函数中不理解的行为

zey*_*cus 2 python lambda list-comprehension

我不理解一段代码的行为,涉及一个在不同对象中调用方法的lambda函数的理解列表.它发生在一个大型程序中,所以对于这个问题,我做了一个荒谬的玩具案例来表明我的观点:

class Evaluator(object):
    def __init__(self, lft, rgt):
        self.lft = lft
        self.rgt = rgt

    def eval(self, x):
        return self.lft + x * (self.rgt - self.lft)


if __name__ == "__main__":
    ev1 = Evaluator(2, 3)
    ev2 = Evaluator(4, 5)
    ev3 = Evaluator(6, 7)
    funcs = [lambda x:ev.eval(x+0.1) for ev in (ev1, ev2, ev3)]
    print([f(0.5) for f in funcs])
Run Code Online (Sandbox Code Playgroud)

我得到的输出是[6.6, 6.6, 6.6],这意味着它是ev3一直被评估的方法.而不是[2.6, 4.6, 6.6]像我期望的那样.但令我惊讶的是,如果我摆脱lambda函数,行为就好了:

class Evaluator(object):
    def __init__(self, lft, rgt):
        self.lft = lft
        self.rgt = rgt

    def eval(self, x):
        return self.lft + x * (self.rgt - self.lft)

if __name__ == "__main__":
    ev1 = Evaluator(2, 3)
    ev2 = Evaluator(4, 5)
    ev3 = Evaluator(6, 7)
    funcs = [ev.eval for ev in (ev1, ev2, ev3)]
    print([f(0.5) for f in funcs])
Run Code Online (Sandbox Code Playgroud)

回报[2.5, 4.5, 6.5].谁能解释一下这里发生了什么?我应该如何以Pythoninstic方式编写代码?

Bry*_*ley 5

问题是你在调用函数时只评估ev .因此,ev它只在您启动print语句时使用任何值.当然,到那个时候ev有列表中最后一个函数的值.

这与你做过这个没什么不同:

funcs = [lambda x: ev.eval(x+0.1),
         lambda x: ev.eval(x+0.1),
         lambda x: ev.eval(x+0.1)]
Run Code Online (Sandbox Code Playgroud)

请注意,它们是如何使用ev,他们都将使用相同 ev的运行功能的时间.

要做你想做的事,你需要ev定义理解时绑定到列表理解中的当前值,你可以通过传递lambda参数来做到这一点:

funcs = [lambda x, ev=ev: ev.eval(x+0.1) for ev in (ev1, ev2, ev3)]
Run Code Online (Sandbox Code Playgroud)

但是,我强烈建议你要这样做.正如您刚刚经历的那样,这样的代码很难理解和调试.你不会因为尽可能多的功能填充到一行而赢得任何积分.

技术术语是关闭.有关更多信息,请查看stackoverflow上的这个问题: 为什么不将python嵌套函数称为闭包?