以下是我对于python装饰器的一些个人心得,拿出来分享给大家
什么是装饰器
- 在python中装饰器的功能是将被装饰的函数作为参数传递给与装饰器对应的函数(一般是名字相同的函数),并返回包装后的被装饰函数。
- 装饰器的作用:增强被装饰函数的功能,做到开放封闭原则,也就是说在不修改被装饰函数源码的同时实现功能增强
本质
在我的认知中,我认为python装饰器本质上是闭包函数的语法糖,那什么是闭包函数什么是语法糖呢
闭包
闭包函数在python里就是:如果在一个内部函数里,对外部作用域但不是全局作用域的变量的引用(简而言之就是在一个嵌套函数里,内部函数去调用外部函数的局部变量(不是全局变量)),那么这个内部函数就被认为是闭包
语法糖
一种语法,不会影响原格式的功能,只是为了让程序员更方便使用,可读性更高
如何实现
假如说我们有这么一个函数
def eat():
print("我要开始吃了")
我们想给这个函数增加一个日志功能,我们首先想到的是这样
def eat():
print("函数调用开始")
print("我要开始吃了!")
print("函数调用结束")
这样看函数是不是特别臃肿,而且他入侵到原来的eat()函数里面,导致有些逻辑可能变得复杂,同时也不符合“一个函数实现一件事情”的原则
为了使得eat()增加一个日志的功能,我们可以使用装饰器来实现
def log(fun):
def wrapper():
print("函数开始调用")
fun()
print("函数调用结束")
return wrapper
def eat():
print("我要开始吃了!")
eat = log(eat)
eat()
这时候log(eat)将eat函数作为参数传递给log(),由于wrapper()函数是log()的闭包,所以他可以去调用外部函数log()的局部变量fun,也就是传递进来的eat,fun()就在返回函数wrapper()的内部执行
不过这里的eat = log(eat)看着总有些别扭,而且每一次去调用都得重复地去写,比较麻烦。
所以我们这里引用语法糖,这样看起来是不是就简洁明了啦
def log(fun):
def wrapper():
print("函数开始调用")
fun()
print("函数调用结束")
return wrapper
@log ##语法糖,这里等价于eat = log(eat)
def eat():
print("我要开始吃了!")
eat()
不仅如此,我们还可以实现带参数的装饰器,只需要将参数扔给内部的wrapper()函数
def log(fun):
def wrapper(name):
print("函数开始调用")
fun(name)
print("函数调用结束")
return wrapper
@log ##语法糖,这里等价于eat = log(eat)
def eat(name):
print("我要开始吃%s了!" % name)
eat("饺子")
eat("咸鱼")
如果传入的参数不定长,那么我们可以使用收集参数去接收
def log(fun):
def wrapper(*args, **kwargs):
print("函数开始调用")
fun(*args, **kwargs)
print("函数调用结束")
return wrapper
多个装饰器装饰一个函数
多个装饰器执行的顺序就是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身
下面这段代码函数执行顺序:fun2() --> fun1() --> eat()
def fun1(fun):
def wrapper():
print("this is fun1")
fun()
print("fun1 end")
return wrapper
def fun2(fun):
def wrapper():
print("this is fun2")
fun()
print("fun2 end")
return wrapper
@fun1
@fun2
def eat():
print("我要开始吃了!")
if __name__ == '__main__':
eat()
装饰器这里手动等价于
eat = fun2(eat)
eat = fun1(eat)
eat()
--------------------------------或者------------------------------------------------
eat = fun1(fun2(eat))
eat()