我想玩匿名函数,所以我决定做一个简单的寻找者。这是:
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
但我发现,每次都会访问i
中的lambda x:x%i==0
,而我希望它是一个字面数字。我怎样才能让它成为lambda x:x%3==0
?
答案 0 :(得分:40)
创建lambda
时,您可以“捕获”i
lambda x, i=i: x%i==0
这将设置lambda上下文中的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()
答案 1 :(得分:6)
问题是tests
中的每个函数都指的是变量i
。
更常见的是,你在函数内部执行此操作,在这种情况下,你有一个本地到定义范围的变量i
,它存储在一个闭包中,如{{3}中所述。 }。
但是在这里,它更简单:i
是一个全局变量,因此没有闭包。编译函数以在运行时将i
作为全局变量进行查找。由于i
已更改,因此函数将在运行时看到更改的值。就这么简单。
传统的解决方法(适用于闭包和全局变量)被称为“默认值黑客”,尽管它并不是真正的黑客攻击。 (见These Nasty Closures。)Ryan Haining的回答解释了如何做到这一点:
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)
在这种情况下,我认为你对其他解决方案更好;如果 合适的情况,请保留这个。
答案 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))