当函数语句被执行时,它们被绑定到它们的(词法上的)封闭范围。
在您的代码段中,lambda 绑定到全局范围,因为for
套件不是作为 Python 中的独立范围单元执行的。在for
循环结束时,num
被绑定在封闭范围内。演示:
for num in range(1, 6):
pass
assert num == 5 # num is now bound in the enclosing scope
for
因此,当您在循环中绑定标识符时,您实际上是在操纵封闭范围。
for num in range(1, 6):
spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope
列表推导的相同处理:
[num for num in range(1, 6)]
assert num == 5
令人兴奋,我知道。任何人,根据我们新发现的知识,我们可以确定您正在创建的 lambda 是指num
绑定在封闭范围内的(单个)标识符。这应该更有意义:
functions = []
for number in range(1, 6):
def fun():
return number
functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)
这是最酷的部分,它更能证明这一点:
# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
# def fun():
# return number
# functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions)
因此,解决方案当然是为每个number
要绑定的对象创建一个新的封闭范围。在 Python 中,您可以使用模块、类和函数创建新的封闭范围。使用一个函数来为另一个函数创建新的封闭范围是很常见的。
在 Python 中,闭包是一个返回另一个函数的函数。有点像函数构造函数。请查看get_fun
以下示例:
def get_fun(value):
""":return: A function that returns :param:`value`."""
def fun(): # Bound to get_fun's scope
return value
return fun
functions = []
for number in range(1, 6):
functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)
既然get_fun
是一个函数,它就有自己的内部作用域。每次get_fun
使用值调用时,都会创建一个小表来跟踪其中的绑定;即它说,“在这个范围内,value
标识符指向被传递的东西。” 该范围在函数执行结束时消失,除非有理由让它徘徊。
如果您要从范围内返回一个函数,那么这就是“范围表”的一部分存在的一个很好的理由——当您稍后调用它时,您返回的函数可能会引用该范围表中的内容。出于这个原因,在Pythonfun
中创建的when 会告知' 范围表,以便在需要时方便使用。get_fun
fun
get_fun
fun
你可以在Python docs on the execution model中阅读更多关于细节和技术术语(我稍微软化了一点)的信息。您还可以使用 来查看函数引用的封闭范围的部分print fun.__closure__
。在上面,我们看到了对 的引用value
,它恰好是一个 int:
# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
# functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)