python装饰器也是从很少之前就接触了,由于都是自己学习,我的应用场景中又很少用到,就一直搁置了,最近又学习了一下。写一下理解,欢迎道友批评指正。
为什么要有装饰器
在实际的应用场景中,需要记录函数的执行过程和函数的运行状态,比如说日志系统、函数的运行时间、函数的异常状态、返回值等。而在项目中,很多函数都需要进行这样的操作,那么怎么来实现呢?
第一种方式:我们可以在每一个函数中,都增加相应的代码,对函数执行状态进行记录。但是这样有一些问题。每个函数中都需要增加与业务功能无关的代码,并且每一个函数都进行这个的功能,不同的人编写各自的业务函数的时候,对于这些通用的功能,代码可能会产生不一致性。容易出错。
第二种方式:就是通过装饰器来实现。把通用的这些功能通过装饰器来实现,业务功能用函数来实现。把业务函数与装饰器结合,可以实现对函数状态的记录。
装饰器长什么样子
我通过以下代码分享一下我对装饰器的理解。
** 定义业务函数**
业务函数中,不需要考虑添加记录函数状态的代码,只专注于业务功能的开发。
def myFunction(timer_set=2):
time.sleep(timer_set)
print("程序执行结束")
定义装饰器函数
在开始介绍装饰器函数前,先介绍一下一下闭包的概念。如果一个函数myDecorator中,定义了另一个函数wrapper,myDecorator函数的返回值是内部定义的函数wrapper,这样就构成了一个闭包。
在闭包中,内部函数可以使用外部函数的变量。
我们这样实现装饰器,如下面代码所示:
我们把记录函数状态的代码写在内部函数wrapper里面,
def myDecorator(func_name):
def wrapper(*args, **kwargs):
st = time.time()
print("{} is running".format(func_name.__name__))
f = func_name(*args, **kwargs)
et = time.time()
print("程序耗时:", et-st)
return f
return wrapper
函数调用
函数调用时,经过装饰器修饰,返回内部函数,而内部函数就是公用功能和业务函数的结合体,就实现了我们的目标。
decorator = myDecorator(myFunction)
decorator()
myFunction is running
程序执行结束
程序耗时: 2.003638505935669
python装饰器的实现方式
@符号函数实现
上述的代码,解释了python装饰器的基本实现,但是我们发现,调用时,还是有些繁琐,需要调用装饰器函数修饰业务函数,多少有些麻烦,有没有更简洁的实现的呢?
答案是有的,python中,可以使用@符号,实现装饰器函数对业务函数的修饰。这个过程在业务函数定义时完成,如代码所示,这样,调用时,就不用考虑装饰器了。当然,实现也很简单。
定义装饰器
这部分代码没有变化
import time
def myDecorator(func_name):
def wrapper(*args, **kwargs):
st = time.time()
print("{} is running".format(func_name.__name__))
f = func_name(*args, **kwargs)
et = time.time()
print("程序耗时:", et-st)
return f
return wrapper
定义业务函数
如以下代码所示,这样的话,可以完成了业务函数的定义,输出结果和前面一样
@MyDecorator
def myFunction(timer_set=2):
time.sleep(timer_set)
print("程序执行结束")
@符号函数实现(带参数)
can_run = False
def myDecorator(param_can_run):
def middle(func_name):
def wrapper(*args, **kwargs):
if param_can_run==True:
st = time.time()
print("{} is running".format(func_name.__name__))
f = func_name(*args, **kwargs)
et = time.time()
print("程序耗时:", et-st)
else:
print("program is not running")
return wrapper
return middle
函数定义
函数定义中,只需要在@代码后面传入参数即可
can_run = True
@myDecorator(can_run)
def myFunction(timer_set=2):
time.sleep(timer_set)
print("程序执行结束")
myFunction(timer_set=2)
这种调用方式,等价于不带@的调用如下进行
can_run = True
f_middle = myDecorator(can_run)
f_wrapper = f_middle(myFunction)
f_wrapper(timer_set=2)
类实现
以上功能可以考虑使用类来实现
定义装饰器
这部分代码没有变化
import time
class MyDecorator(object):
def __init__(self, func_name):
self._func_name = func_name
def __call__(self, *args, **kwargs):
st = time.time()
print("{} is running".format(self._func_name.__name__))
self._func_name(*args, **kwargs)
et = time.time()
print("程序耗时:", et-st)
定义业务函数
如以下代码所示,这代码不需要变化,@符号后面不再是函数名,而是类名
@MyDecorator
def myFunction(timer_set=2):
time.sleep(timer_set)
print("程序执行结束")
python的装饰器工具
以上代码我们把函数说明加上
def myDecorator(func_name):
"""this is decorator """
def wrapper(*args, **kwargs):
"""this is wrapper"""
st = time.time()
print("{} is running".format(func_name.__name__))
f = func_name(*args, **kwargs)
et = time.time()
print("程序耗时:", et-st)
return f
return wrapper
@myDecorator
def myFunction(timer_set=2):
"""this is business function"""
time.sleep(timer_set)
print("程序执行结束")
查看函数文档
myFunction.__doc__
输出如下所示
'this is wrapper'
显然,文档说明不对,python提供了工具,可以对这个问题进行修改,代码修改如下:
from functools import wraps
def myDecorator(func_name):
"""this is decorator """
@wraps(func_name) # 添加此处代码
def wrapper(*args, **kwargs):
"""this is wrapper"""
st = time.time()
print("{} is running".format(func_name.__name__))
f = func_name(*args, **kwargs)
et = time.time()
print("程序耗时:", et-st)
return f
return wrapper