装饰器是什么?
以前并没有怎么注意装饰器,只是用现成的,最近发现,在一些特定的可复用场景下,装饰器的确起到了不小的作用,并且可以大大简化我们的代码。
装饰器的概念
谈到装饰器,就一定离不闭包。那么什么是闭包呢?
闭包
闭包就是能够读取其他函数内部变量的函数。通俗理解可以就是一个高级嵌套的函数。
闭包还有一个概念很重要,那就是作用域!闭包是指可以访问其所在作用域的函数。
我们来看看下面的例子:
def A():
a = 1
def B():
print(a)
return B
你在函数A中定义了函数B,并将B作为A的返回值返回,B中又使用了A中定义的变量,此时就形成了闭包。是不是很拗口?
基本可以通过你中有我我中有你来概括,但又感觉好像有点不对。后来在网上看到一个很有趣的比喻:就是别人家有某个东西,你想拿到但是因为权限不够(不打死你才怪),但是你可以跟家里的孩子关系好,可以通过他拿到!
这个家就是局部作用域,外部无法访问内部变量,孩子是返回对象,对家里的东西有访问权限,借助返回对象间接访问内部变量!
了解了闭包,那么装饰器就比较好理解了,装饰器其实就是一个特殊的集成的闭包。
装饰器的作用
为什么要用装饰器
先来举个例子
import time
def work():
time1 = time.time()
print(f'任务开始,开始时间:{str(time1)}')
do something
print(f'任务结束,耗时:{str(time.time()-time1)}S')
以上是最为常见的一个日志打印。这是在只有一个任务的时候,这样做没什么问题。但是在一个项目中,少则几十多则上千的任务、方法、接口需要打印日志,难道每个都这样写吗?假如我要修改其中一个多加一个打印或者修改一下输出逻辑,还需要每个都修改吗?这时候就轮到我们的主角装饰器登场了
装饰器的简单使用
首先,我们先来定义一个装饰器
import time
def work_log(func):
def log_time():
time1 = time.time()
print(f'任务开始,开始时间:{str(time1)}')
func()
print(f'任务结束,耗时:{str(time.time()-time1)}S')
return log_time
如此一来,一个简单的装饰器就完成了,下面看下怎么使用吧
@work_log
def work():
do something
这时,在调用work方法的时候,会输出响应的日志开始和结束时间的打印
中级装饰器
函数千千万,在实际场景中,大多数函数都带有餐数,还有一些类方法
带参数的又如何使用呢?还是拿打印时间日志来距离
import time
def work_log(func):
def log_time(*args, **kwargs):
time1 = time.time()
print(f'任务开始,开始时间:{str(time1)}')
func(*args, **kwargs)
print(f'任务结束,耗时:{str(time.time()-time1)}S')
return log_time
在这里还得感叹一下,python里的*args, **kwargs
还真是简便,这样一来,带装饰器的函数,不管加了什么参数都可以应对
装饰器的用法一样
@work_log
def work(todo1, todo2):
print(todo1)
do something
print(todo2)
带参数的装饰器
在一些场景中,不管调用的函数有参数,装饰器本身也可以带不同的参数来控制变量(虽然目前我还没遇到过需要我这样做的场景,不过这里就先举个栗子吧)
import time
def logger(level):
def work_log(func):
def log_time(*args, **kwargs):
time1 = time.time()
print(f'{level}:任务开始,开始时间:{str(time1)}')
func(*args, **kwargs)
print(f'{level}:任务结束,耗时:{str(time.time()-time1)}S')
return log_time
return work_log
如此一来,就可以在调用@logger的时候输入增加日志级别的参数,比如这样:
@work_log(INFO)
def work(todo1, todo2):
print(todo1)
do something
print(todo2)
是不是挺简单?
人生苦短,我用python