python闭包与装饰器

闭包

什么是闭包?简单来讲:

内部函数作为返回值给出,外部变量接收绑定后可以使用,闭包可以保存函数内部特定运行的一个状态

形成闭包很容易:

  • 外部函数中定义了内部函数
  • 外部函数是有返回值的
  • 返回的值是内部函数名(其实是其绑定的对象)
  • 内部函数引用了外部函数的变量
1
2
3
4
5
6
7
8
9
10
11
def outer_func():
c = 1
def inner_func(a,b):
d = a+b+c
return d
return inner_func

a = outer_func() #a就是一个闭包,它保存了一个c,c是外部函数的,调用a时就不依赖外部函数了
print(a)
d = a(2,3) #a是内部函数,可调用
print(d)

生成闭包后,内部函数的调用就不依赖外部函数了,它自身已经保存了外部函数的状态,可以在如上程序第三行加上print(“here!”)验证

闭包的总结:

  • 闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成
  • 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
  • 闭包的好处:使代码变得简洁,便于阅读代码
  • 闭包是理解装饰器的基础

装饰器

描述过程可能比较不具体,所以直接上个实例,顾名思义,装饰器是用来装饰一个函数的,它可以大大提升程序的可扩充性

实例

抛出问题

我有一个函数,原来写的,我叫它毛坯房,其实它也只打印出了这个,它一定需要修饰

1
2
def fun1():
print("毛坯房!")

这个函数在我的项目中总共调用了100次,使用量还是挺大的,我想给他增加一些特定的功能,当然这不是什么问题,加就是了,关键我加了100行

1
2
3
4
5
def fun1():
print("毛坯房!")
print("铺地板!")
print("吊顶!")
print("刷墙")

这也不是什么问题,100多次的fun()的调用依然没有什么问题,但是问题来了,我要恢复原来fun()的基本功能,然后新加入新的功能,有点难了,这么多代码纵横交错在一起

当然这也不是什么困难的东西,使用版本管理工具回退历史查看,复制拷贝然后就ok了

但不幸的是,你被告知,对这个函数这样的更改还有很多次,被改的函数还有很多个,你有点儿慌了,这是什么事儿啊!

我花费很多功夫,做的事情好多好像失去了编程的意义。你陷入了沉思

解决问题

有没有一种写法,不用更改我原来的fun(),保持它的基本功能,使用另一个函数来修饰它;不同的修饰可以写成不同的函数,使用过后还可以复用。然后使用一个函数来装饰就像给函数戴个帽子一样,方便脱戴。

python的装饰器来了

解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def decorate1(fun):  #这个装饰器:给他铺地板
def wrapper():
fun()
print("铺地板!")
return wrapper

def decorate2(fun): #这个装饰器:给他刷墙
def wrapper():
fun()
print("刷墙!")
return wrapper

@decorate1
def function():
print("毛坯房!")

function()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def decorate1(fun):
def wrapper():
fun()
print("铺地板!")
return wrapper

def decorate2(fun):
def wrapper():
fun()
print("刷墙!")
return wrapper

@decorate2
def function():
print("毛坯房!")

function()

使用了不同的装饰器,给function()增加了不同的额外功能

详解

如图显示了函数名作为参数传递的过程,解释了它为什么能够装饰一个函数

  • function()是被装饰函数
  • 将被装饰函数作为参数传给装饰器decorate
  • 执行decorate函数
  • 将返回值又赋值给function

而装饰一个函数只需要给一个函数头上加上@装饰器名

它明显可以解决上面我抛出的问题

多层装饰器

多层装饰器,谁距离近,就优先调用谁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def derocate1(fun):
def wrapper():
fun()
print("铺地板")
return wrapper

def derocate2(fun):
def wrapper():
fun()
print("刷墙")
return wrapper

@derocate1
@derocate2
def func():
print("毛坯房!")

func()

从这里我们可以看到多层装饰器的执行过程

带参数的装饰器

需要多写一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
def put_(a):
def decorate(func):
def wrapper(*args,**kwargs):
func(*args)
print("铺地板砖,铺{}块儿!".format(a))
return wrapper
return decorate

@put_(10)
def funct(*args):
print("毛坯房!")

funct()

实例推荐

没看完这11 条,别说你精通 Python 装饰器


python闭包与装饰器
https://blog.wangxk.cc/2019/09/15/python闭包与装饰器/
作者
Mike
发布于
2019年9月15日
许可协议