lambda函数访问外部变量

时间:2014-01-10 20:39:42

标签: 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

但我发现,每次都会访问i中的lambda x:x%i==0,而我希望它是一个字面数字。我怎样才能让它成为lambda x:x%3==0

3 个答案:

答案 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))