关于python:lambda函数访问外部变量

lambda function acessing outside variable

本问题已经有最佳答案,请猛点这里访问。

我想玩匿名函数,所以我决定做一个简单的Prime Finder。这里是:

1
2
3
4
5
6
7
8
9
10
11
12
tests = []
end = int(1e2)
i = 3
while i <= end:
    a = map(lambda f:f(i),tests)
    if True not in a:
        tests.append(lambda x:x%i==0)
        print i
    print tests
    print"Test:"+str(i)
    print str(a)
    i+=2

不过,我发现,每次访问lambda x:x%i==0中的i,而我希望它是一个文字数字。我怎样才能让它变成lambda x:x%3==0


创建lambda时可以"捕获"i

1
lambda x, i=i: x%i==0

这将使lambda上下文中的i等于创建时的i。你也可以说lambda x, n=i: x%n==0,如果你想的话,它不完全是捕获,但它能满足你的需要。

如果没有这个,正如您所看到的,它将在封闭范围内查找i

这是一个查找问题,类似于以下定义函数的问题:

1
2
3
4
5
6
7
8
9
10
11
12
i ="original"

def print_i1():
    print(i) # prints"changed" when called below

def print_i2(s=i): #default set at function creation, not call
    print(s) # prints"original" when called below


i ="changed"
print_i1()
print_i2()


问题是,tests中的每个函数都引用了变量i

更常见的是,您在函数内部这样做,在这种情况下,您有一个定义范围变量i,它存储在一个闭包中,正如在这些讨厌的闭包中所解释的那样。

但这里更简单:i是一个全局变量,所以没有闭包。这些函数被编译为在运行时将i作为全局变量查找。由于i已更改,函数运行时将看到更改的值。很简单。

围绕这一点的传统方法(同时适用于闭包和全局变量)被亲切地称为"默认值黑客",尽管它不是真正的黑客。(参见常见问题解答中的解释)Ryan Haining的回答解释了如何做到这一点:

1
lambda x, i=i: x%i==0

这将创建一个名为i的参数,其默认值等于创建函数时i的值。然后,在函数内部,当您访问参数i时,您会得到这个值。

解决这个问题的另一种方法是,创建一个函数创建函数,并将EDOCX1的值(0)作为参数传递给函数创建函数,这在用户2864740的回答中似乎更为常见:

1
(lambda i: lambda x: x%i)(i)

这避免了用一个额外的参数"污染"函数的签名(有人可能意外地将参数传递给),但代价是创建和调用一个函数没有任何好的理由。

第三种方法是使用partial。在您所要做的只是部分应用一个函数的情况下,使用partial而不是将包装函数定义为lambda可能更干净。

不幸的是,在这种情况下,函数隐藏在一个运算符内,而公开它的函数operator.mod不接受关键字参数,因此不能有效地分割它的第二个操作数。所以,在这种情况下,这是一个糟糕的解决方案。如果您真的想这样做,您只需编写一个性能更好的包装器,partial就可以:

1
2
3
4
def opmod(a, b):
    return a % b

partial(operator.mod, b=i)

在这种情况下,我认为你最好选择其他的解决方案;只要把这一个放在你的脑子里,以备不时之需。


创建返回lambda的新函数。然后称之为,传入i作为参数。这将创建一个新的绑定范围。

1
2
3
4
5
6
7
def make_test (i):
   # this i refers to the parameter (which evaluates to the /value/ passed)
   return lambda x: x%i==0

# ..
# the /value/ resulting from evaluating the variable is passed
tests.append(make_test(i))