什么是闭包?大多数人对它的印象就是函数中嵌套函数吧。本篇就来详细的介绍闭包及其作用。
首先要从初中所学的一个直线方程y = kx + b
讲起了……
对于给定的k,b
,y 会随着x的改变而改变,那如何用编程实现呢?
学会函数的你一开始是这样写的:
def line(k, b, x):
return k * x + b
line(1, 2, x=1)
line(1, 2, x=2)
line(1, 2, x=3)
但这种方式是有一个很大的缺点,对于给定的k, b
,我只想改变x的值,这种情况下就没必要反复传递k和b了
好,学会类的你又开始这么写了:
class Line(object):
def __init__(self, k, b):
self.k = k
self.b = b
def __call__(self, x, *args, **kwargs):
return self.k * x + self.b
line = Line(1, 2)
print(line(1))
print(line(2))
print(line(3))
这种实现方式就明显比函数来的方便明了。但还是有问题:
对于不同的k, b
,需要生成多个实例对象,太占用资源。(实例对象要等到程序结束才释放)
那有没有一种更轻量级的实现方式呢? 有,就是闭包
def line(k, b):
def closure(x):
return k * x + b
return closure
line = line(1, 2)
print(line(1))
print(line(2))
print(line(3))
以上就是一个简单的闭包。解释一下闭包的概念:
内部函数用到了外部函数的变量,那么就将内部函数的代码与外部函数的变量统一封装在一起,然后返回内层函数的情况, 这就是闭包。
闭包的调用与类相似, 所以闭包也可以理解为一种“轻量级的对象”
这里line
函数返回的是一个内部函数对象,因为Python中,函数名也作为对象,可以作为line
函数的返回值。
所以line
变量(注意line
不再是函数)指向了closure
这个对象的地址,调用line(1)
实际上就调用了closure(1)
函数在调用完即会释放,但如果内函数引用到了外部函数的变量,这些变量是不会释放的。
恰当地使用闭包能极大提高程序的运行与开发效率。那使用闭包还有哪些好处呢?
- 对于参数是不可变对象时,可以有效避免使用全局变量, 如下代码所示:
def f(x):
def closure():
nonlocal x
x = x + 1
closure()
print("修改后的 x =", x)
f(1)
修改外部的不可变对象时,需要用nonlocal
关键字指明该变量是外部变量。将x
重新指向了计算后的结果2
。
这样把x
封装在函数内部也说明了x
是f()
的特有变量,就好比是类的实例变量。
- 而对于参数是可变对象时,使用闭包可以保留之前运行结果的状态。
def move(pos):
def closure(dir, step):
pos[0] = pos[0] + dir[0] * step
pos[1] = pos[1] + dir[1] * step
return pos
return closure
move = move([0, 0]) # 起始点(0, 0)
print(move((1, 0), 1)) # 先向x轴正方向移动一步
print(move((0, 1), 2)) # 再向y轴正方向移动两步
print(move((-1, 0), 2)) # 最后向x轴负方向移动两步
得到运行结果:
[1, 0]
[1, 2]
[-1, 2]
以上就是模拟从0,0
点朝上下左右不断移动的过程。
- 最后当参数是一个函数对象时,就是装饰器。
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
value = func(*args, **kwargs)
print("函数消耗时间为:", time.time() - start)
return value
return wrapper
def run(x):
time.sleep(2)
return x
run = timer(run)
print(run(2))
如上就是一个计算函数消耗时间的装饰器:在不修改原函数的代码与调用方式的前提下,增加原函数的功能
这里不过多的介绍装饰器,总之学好闭包是掌握装饰器的前提。
看到这你也大致了解闭包的用处了。
本篇结束。