从一个小问题开始
前几天在写代码的时候需要在循环中定义函数,却出现了预料之外的现象。先来看看下面这段简单的代码:
def generate_funcs():
funcs = []
for i in range(5):
funcs.append(lambda: print(i))
return funcs
for f in generate_funcs():
f()
在循环过程中定义函数,我们希望5个函数分别输出0、1、2、3、4,即期望的输出是这样的:
0
1
2
3
4
然而运行结果是下面这样的:
4
4
4
4
4
大家不禁会纳闷儿了,我定义函数的时候明明白白写着 print(i)
,i
的值从 0 循环到 4,怎么执行出来就打印的都是 4 了呢?没错,这就是 Python 闭包中的 late binding 机制在“作祟"。这篇文章我们就好好讲讲 Python 闭包和 late binding 机制,避免大家再次踩坑。
从闭包 (closure) 谈起
我们要先介绍嵌套函数 (nested function) 和非局部变量 (non-local variable) 的概念。
定义在函数内部的函数叫做嵌套函数。我们都知道,函数有作用域,嵌套函数既可以访问自身作用域的变量,也可以访问外层函数作用域中的变量。看一个例子:
def print_function(msg):
# printer is a nested function
def printer():
print(msg)
printer()
print_function("Hello world!")
输出为
Hello world!
这里 printer
是一个嵌套函数,通过程序输出我们可以发现 printer
可以访问到外层函数 print_function
作用域中的 msg
变量。我们通常把函数参数或者定义在函数内部的变量叫做函数的局部变量 (local variable),而对于 printer
这个嵌套函数,msg
可以被访问到,但并不是 printer
的局部变量(而是外层函数的局部变量),我们把 msg
这样被嵌套函数访问的外层函数的局部变量称为嵌套函数的非局部变量 (non-local variable)。
我们把上面的程序稍作修改,得到下面的程序:
def print_function