2014-01-10 136 views
14

我想玩匿名函数,所以我决定做一个简单的主要发现者。那就是:lambda函数访问外部变量

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==0i每次访问,而我希望它是一个字面上的数字。我怎样才能让它变成lambda x:x%3==0呢?

+1

另请参阅[这些讨厌的闭包](http://code.activestate.com/recipes/502271/)比在官方文档中更好的解释。但简短的版本是,这些测试函数中的每一个实际上都是围绕同一个变量'i'的闭包,而'i'保持不断变化的值。 (这是不准确的,因为全局变量实际上并不需要存储在闭包中,但效果是一样的。) – abarnert

回答

27

可以“捕获” i创建时的拉姆达

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

这将设置i在拉姆达的情况下等于任何它被创建时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() 
+0

哦,聪明。 – user2864740

+0

+1非常好:) –

+2

这是可行的,因为当函数被创建*时,默认参数被评估,而当函数被*调用*时,变量查找被完成。 –

2

创建一个返回lambda的新函数。然后调用它,传递i作为参数。这将创建一个新的绑定范围。

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)) 
6

问题是tests中的每个函数都指的是变量i

更常见的是,您可以在函数内部执行此操作,在这种情况下,您有一个局部定义范围的变量i,它将存储在闭包中,在These Nasty Closures中有很好的解释。

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


解决这个问题的传统方式(这同时适用于封闭和全局变量)被亲切地称为“默认值黑客”,即使它不是一个真正的黑客。 (见the explanation in the FAQ。)瑞安海宁的回答解释了如何做到这一点:

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

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


一种不同的方式解决这个问题,如果您使用的是像JavaScript语言可能看起来比较熟悉,是创建一个函数创建功能,并传递的i值作为参数传递给该功能 - 创建功能,如user2864740的回答是:

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

这避免“污染”功能的签名与一个额外的参数(有人可能会不小心将参数传递到),但在创建和调用函数的成本没有很好的理由。


解决此第三种方法是使用partial。如果您所要做的只是部分应用功能,则使用partial而不是定义包装函数,因为lambda可能更清晰。

不幸的是,在这种情况下,函数隐藏在一个运算符中,并且函数operator.mod公开它并不带关键字参数,所以你不能有用地部分分配它的第二个操作数。所以,在这种情况下这是一个不好的解决方案。如果你真的想,你可以只写一个表现更好的包装和partial是:

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

partial(operator.mod, b=i) 

在这种情况下,我认为你是与其他解决方案更好;只要保留这个在你的脑袋适当的情况下。

相关问题