lambda函数访问外部变量

Hov*_*tar 16 python scope

我想玩匿名函数,所以我决定做一个简单的寻找者.这里是:

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
Run Code Online (Sandbox Code Playgroud)

然而,我发现了什么,就是ilambda x:x%i==0每次访问,而我希望它是一个字面上的数字.我怎么能让它变成lambda x:x%3==0呢?

Rya*_*ing 44

你可以i在创建lambda时"捕获"它

lambda x, i=i: x%i==0
Run Code Online (Sandbox Code Playgroud)

这将i在lambda的上下文中设置等于i创建它时的任何内容.你也可以说,lambda x, n=i: x%n==0如果你想要,它不是完全捕获,但它可以满足你的需要.

如果没有这个,正如你所见,它将i在封闭范围内寻找一个


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

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()
Run Code Online (Sandbox Code Playgroud)

  • 这是有效的,因为默认参数是在函数*created*时计算的,而变量查找是在函数被*调用*时完成的. (3认同)

aba*_*ert 6

问题是这些函数中的每一个tests都是指变量i.

更常见的是,你在一个函数内部执行此操作,在这种情况下,你有一个local-to-the-defined-scope变量i,它存储在一个闭包中,正如在这些讨厌的闭包中很好地解释的那样.

但在这里,它甚至更简单:i是一个全局变量,因此没有闭包.编译这些函数以i在运行时查找为全局变量.自i更改后,函数将在运行时看到更改的值.就那么简单.


围绕它的传统方式(适用于闭包和全局变量)被人们称为"默认值黑客",尽管它并不是真正的黑客攻击.(请参阅常见问题解答中的说明.)Ryan Haining的答案解释了如何执行此操作:

lambda x, i=i: x%i==0
Run Code Online (Sandbox Code Playgroud)

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


如果你习惯使用像JavaScript这样的语言,这可能看起来更熟悉的另一种方法是创建一个函数创建函数,并将i作为参数的值传递给该函数创建函数,如user2864740的答案:

(lambda i: lambda x: x%i)(i)
Run Code Online (Sandbox Code Playgroud)

这避免了使用额外参数(有人可能意外地将参数传递给)来"污染"函数的签名,但是代价是无缘无故地创建和调用函数.


第三种方法是使用partial.如果你想要做的就是部分应用一个函数,那么使用partial而不是将包装器函数定义为lambda可以更清晰.

不幸的是,在这种情况下,函数隐藏在运算符中,并且operator.mod公开它的函数不接受关键字参数,因此您无法有效地部分其第二个操作数.所以,在这种情况下,这是一个糟糕的解决方案.如果你真的想要,你可以写一个行为更好的包装器,并且partial:

def opmod(a, b):
    return a % b

partial(operator.mod, b=i)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我认为你对其他解决方案更好; 只要保持这一个在你的头的情况下它适当的.