如何使用循环创建一致的lambda函数?

时间:2016-03-09 03:22:51

标签: python lambda

我想创建几个类似的lambda函数。

当我使用简单的循环时,我得不到我期望的结果。

代码:

funcList = []
for i in range(2):
    f = lambda x: i*x
    print("Before: ", f(10))
    funcList.append(f)

print("After: ", funcList[0](10), funcList[1](10))

输出:

Before:  0
Before:  10
After:  10 10

代码:

funcList = [lambda x: i*x for i in range(2)]
print("After: ", funcList[0](10), funcList[1](10))

输出:

After:  10 10

如何创建几个lambda函数,使用" original" i的值,而不是i的最后已知值?

2 个答案:

答案 0 :(得分:1)

问题如下:Python中的闭包(请参阅lambdas vs closures)关闭其环境,将变量名称映射到对象,并且他们通过将封闭的环境添加到其作用域查找链来实现此目的。考虑一个更简单,更明确的例子:

import sys
def foo():
    result = []
    for i in range(2):
        def bar():
            return i
        result.append(bar)
    return result

foo的范围查找链,在它返回之前具有环境,如下所示:

- "foo" local variables: {i: 1, result: [bar-function-1, bar-function-2], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}

也就是说,如果foo尝试使用变量z,则首先查看第一个环境(foo的局部变量)。如果它在那里,查找成功。如果没有,它将移动到下一个(全局变量)。如果它在那里,查找成功。如果没有,那么链上就没有其他环境了,所以你得到NameError

在这种情况下,resulti会被找到为局部变量foosys,也许其他人会被发现为全局变量,而其他所有变量都会被找到给NameError

每个bar中的范围查找链类似于:

- "bar" local variables: {}
- "foo" local variables: {i: 1, result: [bar-function-1, bar-function-2], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}

最重要的是,foo的本地变量环境被复制到bar的本地变量环境中。因此,bar可以查找i,但它首先在bar的局部变量中找不到它,然后在范围链中找到它并在其中找到它foo的局部变量。因此,当定义第一个bar函数时,其范围查找链如下所示:

- "bar" local variables: {}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {foo: foo-function, sys: sys-module, etc...}

但是,当foo更改其本地变量i时,bar的范围查找链现在看起来像这样:

- "bar" local variables: {}
- "foo" local variables: {i: 1, result: [bar-function-1], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}

所以现在当bar查找i它再次无法在其局部变量中找到它时,查找范围查找链中的一个,并找到foo' s局部变量i ...现在为1,因为它与之前的i相同。

我在评论中写的伎俩有点像黑客。为了更明确一点,请考虑:

def foo():
    result = []
    for i in range(2):
        def bar(j=i):
            return j
        result.append(bar)
    return result

真正发生的事情是您使用参数bar声明j,其默认值设置为{{ i 1}}(即i在定义时引用的对象 ...而不是i随时引用的对象{{1} }在j 中使用。因此,第一个bar函数的范围查找链如下所示:

bar

当循环再次出现时,它看起来像这样:

- "bar" local variables: {j: 0}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {foo: foo-function, sys: sys-module, etc...}

在这两种情况下,- "bar" local variables: {j: 0} - "foo" local variables: {i: 1, result: [bar-function-1], bar: bar-function-2} - global variables: {foo: foo-function, sys: sys-module, etc...} 的查找会立即在j的本地变量中成功,而bar永远不会发生变化。

执行以下操作有点骇人听闻,因为它隐藏了外部j并使其看起来像是指同一i

i

但实际上,它们是两个不同的def foo(): result = [] for i in range(2): def bar(i=i): return i result.append(bar) return result

i

并且,在循环的第二次迭代中:

- "bar" local variables: {i: 0}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {foo: foo-function, sys: sys-module, etc...}

可能更多"适当"方法是做类似的事情:

- "bar" local variables: {i: 0}
- "foo" local variables: {i: 1, result: [bar-function-1], bar: bar-function-2}
- global variables: {foo: foo-function, sys: sys-module, etc...}

在这种情况下,范围链是:

def make_bar(j):
    def bar():
        return j
    return bar

def foo():
    result = []
    for i in range(2):
        bar = make_bar(i)
        result.append(bar)
    return result

并且,在循环的第二次迭代中:

- "bar" local variables: {}
- "make_bar" local variables: {j: 0}
- "foo" local variables: {i: 0, result: [], bar: bar-function-1}
- global variables: {make_bar: make_bar-function, foo: foo-function, sys: sys-module, etc...}

在这种情况下,它有效,因为- "bar" local variables: {} - "make_bar" local variables: {j: 0} - "foo" local variables: {i: 1, result: [], bar: bar-function-2} - global variables: {make_bar: make_bar-function, foo: foo-function, sys: sys-module, etc...} make_bar调用,i被称为将其局部变量make_bar设置为对象{{1} }指在通话时(即j)。当i 0发生变化时,make_bar的本地变量j不会发生变化。

要在您的案例中做一个完全明确的例子,您可以执行以下操作:

foo

或者,@ShadowRanger commented

i

或者只是:

funcList = []
for i in range(2):
    def make_f(j):
        def f(x):
            return j * x
    funcList.append(make_f(i))

或者你可以使用我提出的黑客,现在知道其工作原理的完整故事:

funcList = []
for i in range(2):
    funcList.append((lambda j: lambda x: j*x)(i))

答案 1 :(得分:0)

这是由于后期绑定。

  1. 您已创建了2个功能的列表
  2. 这些功能是什么? - fun(x):return i * x
  3. 在这个阶段,python不关心i或x的值
  4. 但是在循环结束时i的价值是多少? '我'应该是1
  5. 当您致电funcList[0](10)时,它将返回i * x
  6. 此处x为10且i为1,因此对funcList[0](10)的所有调用都将返回10
  7. 答案已在评论f = lambda x,i=i: i*x中给出,它将避免后期绑定,i的值将在创建函数对象时得到解决。