3 道练习题·预计 25 分钟·做对一题解锁下一段
这一节我们通过解决一个实际需求来认识闭包。
我们需要一直记录自己的学习时间,以分钟为单位。比如:
212(累加)最直觉的写法是用一个全局变量记录时间,然后用一个函数往里加:
time = 0
def insert_time(min):
time = time + min
return time
print(insert_time(2))
print(insert_time(10))认真想一下,这段代码会有什么问题呢?
其实,这段代码在 Python 里会报错:
UnboundLocalError: local variable 'time' referenced before assignment
为什么?因为在 Python 中,如果一个函数里给一个名字赋值了,这个名字就被认为是局部变量——哪怕外面有同名的全局变量。函数内 time = time + min 这一行,time 已经被当成局部变量了,但右边又先去取它的值,所以报「先用后定义」。
如果确实要在函数中引用并修改全局变量,可以用 global 关键字:
time = 0
def insert_time(min):
global time
time = time + min
return time
print(insert_time(2))
print(insert_time(10))输出:
2
12
可是啊,这里使用了全局变量——开发中能避免就尽量避免。
为什么?
那有没有更优雅的办法呢?这就是闭包登场的时候了。
我们直接看代码:
def study_time(time):
def insert_time(min):
nonlocal time
time = time + min
return time
return insert_time
f = study_time(0)
print(f(2))
print(f(10))输出:
2
12
这就是闭包——「函数里嵌套函数,内层函数返回出去带着外层变量」。
当一个内部函数被当作对象返回时,它夹带了外部函数的局部变量,就形成了一个闭包。
换句话说:内部函数的局部作用域中可以访问外部函数局部作用域中的变量,这种行为就叫闭包(Closure)。
回头看上面的代码:
def study_time(time): # 外层函数:接收一个 time
def insert_time(min): # 内层函数:实现累加逻辑
nonlocal time
time = time + min
return time
return insert_time # 外层返回内层函数对象(在 Python 中,函数也是对象)
f = study_time(0) # 调用 study_time,f 现在等于 insert_time 函数
print(f(2)) # 相当于调用 insert_time(2)
print(f(10)) # 相当于调用 insert_time(10)f 不是数字,不是字符串——它是一个带着 time 这个变量的函数。每次调用 f 时,那个 time 还在它身边,所以累加可以一直延续。
听起来挺玄,下一节我们会重点讲让闭包修改外层变量的关键字 nonlocal,并做几道练习。
请定义一个函数 make_counter():
count = 0counter(),每次调用 counter() 时让 count + 1 并返回新值(记得用 nonlocal)然后:
c = make_counter()c(),每次都 print 它的返回值输出应该是:
1
2
3