一、定义与应用场景
-
装饰器
:在不修改被装饰对象的原代码以及调用方式的前提下为被装饰对象添加新功能的可调用对象
-
即:
给现有的模块加上一些小功能(小功能可能好多模块都会用到), 但又不让这个功能侵入到原有的代码里去
-
说人话
在不修改原函数的代码的情况下,添加新的功能
;调用函数的函数,传入的是一个函数,返回的也是一个函数
添加的位置:
可以在执行原函数之前加,也可也在执行原函数之后添加 -
装饰器的功能
引入日志 函数执行时间统计 执行函数前预备处理 执行函数后清理功能 权限校验 缓存 def decorate_func(func): # 传入寒暑 def wrapper(): return 100 return wrapper # 返回函数名 @decorate_func def hello(): return 200 func2 = decorate_func(hello) print(hello()) 100
decorate_func(func) 返回了 wrapper() 函数, 所以,hello 其实变成了 wrapper 的一个变量, 而后面的 hello() 执行其实变成了 wrapper()
把一个函数当参数传到另一个函数中,然后再回调, 把 decorator 这个函数的返回值赋值回了原来的 func
二、简单示例与调用过程
-
装饰器只能在调用原函数之前 或者之后 添加功能,而不能在函数的中间添加功能
-
只要用装饰器装饰了的函数,那么不管被调用多少次,都是装饰之后的效果
-
紧挨着的装饰器先执行,执行完成,交给外部的装饰器装饰
import time def decorator_time(func): def wrapper(*args, **kvargs): start_time = time.time() # ----->函数运行前时间 func(*args, **kvargs) end_time = time.time() # ----->函数运行后时间 cost_time = end_time - start_time # ---->运行函数消耗时间 print("%s消耗时间为%s" % (func.__name__, cost_time)) return cost_time return wrapper # ---->装饰器其实是对闭包的一个应用 def check_auth(func): def wrapper(*args, **kwargs): print("这是新添加的 权限校验功能") ret = func(*args, **kwargs) return ret return wrapper def parame_log(func): def wrapper(*args, **kwargs): print("这是新添加的 用户提交参数日志功能") ret = func(*args, **kwargs) return ret return wrapper @check_auth #添加权限的装饰器 @parame_log #添加log日志的装饰器 @decorator_time #装饰器名称就是上面函数名称 def my_function(): return 'Hello world!' if __name__ == '__main__': t = my_function() print(t)
简单解释一下
- 装饰器
decorator_time
本身是一个函数,要装饰什么函数? - 一看, 原来要装饰
my_function
函数, 于是将my_function
函数当做参数传入到decorator_time
函数中 decorator_time
内部又定义了一个函数wrapper
,等一下,wrapper
这个函数有毛用?wrapper
接受的参数又是是的参数?- 哦偶!原来
wrapper
函数才是真正对my_function
函数起装饰作用的。wrapper
接受参数是my_function
的参数不变 decorator_time
怎么与两个返回值?
内部函数wrapper
返回的my_function
返回值,将其返回到外部函数中decorator_time
外部decorator_time
函数返回值,就返回啦
- 装饰器
三、参数化装饰器
就是在上述的装饰情况下,再次添加一层函数,可用来传递参数
def tags(tag):
def set_decorate(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
res = res + tag
return res
return wrapper
return set_decorate
@tags('1024') 可传参数
def my_function():
return 'Hello world!'
if __name__ == '__main__':
t = my_function()
print(t)
四、functools.wraps() 功能与作用
被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)
加上functools的wrap,它能保留原有函数的名称和函数属性
functools.wraps
以保留被装饰函数的元数据
import time
import functools
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kvargs):
start_time = time.time()
func(*args, **kvargs)
end_time = time.time()
cost_time = end_time - start_time
print("%s消耗时间为%s" % (func.__name__, cost_time))
return cost_time
return wrapper
@my_decorator
def my_function(arg1, arg2):
print(f"Inside the function with arg1: {arg1} and arg2: {arg2}")
my_function(10, 20)
print("Function name:", my_function.__name__)
print("Function docstring:", my_function.__doc__)
在这个修改后的示例中,我们去掉了@functools.wraps(func)这一行
当我们调用my_function(10, 20)并打印函数名和文档字符串时
我们可以看到函数名变成了wrapper,而文档字符串变成了None,这说明元数据丢失了
五、类装饰器
- 初始化方法时保存传递函数的本身,
- 在call 方法中执行 保存的函数。执行之前、或之后可进行额外的操作
call将一个类的实例可以像函数调用import time class Timer: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): start = time.time() ret = self.func(*args, **kwargs) print(f"花费时间: {time.time() - start}") return ret @Timer def login(username, password): print(f"login: username: {username}, password: {password}") login('zhangsan', '123456') # 上面装饰器代码等价于下面 def login2(username, password): print(f"login2: username: {username}, password: {password}") userlogin = Timer(login2) userlogin('zhangsan', '123456')