访问外部变量的 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

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

原文由 Hovestar 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 531
2 个回答

您可以在创建 lambda 时“捕获” i

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

这将在 lambda 的上下文中将 i 设置为等于创建时的 i 。如果需要,您也可以说 lambda x, n=i: x%n==0 。它不完全是捕获,但它可以满足您的需求。


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

 glob = "original"

def print_glob():
    print(glob) # prints "changed" when called below

def print_param(param=glob): # default set at function creation, not call
    print(param) # prints "original" when called below

glob = "changed"
print_glob()
print_param()

原文由 Ryan Haining 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

更常见的是,您在一个函数内执行此操作,在这种情况下,您有一个局部到定义范围的变量 i ,它存储在一个闭包中,如 这些讨厌的闭包 中很好地解释的那样。

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


解决这个问题的传统方法(适用于闭包和全局变量)被亲切地称为“默认值 hack”,尽管它并不是真正的 hack。 (请参阅 常见问题解答中的解释。)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)

在这种情况下,我认为您最好使用其他解决方案;在适当的情况 _下_,只需将此保留在您的脑海中即可。

原文由 abarnert 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题