python中的装饰器
一、什么是装饰器
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。简单来讲,可以不严谨地把Python的装饰器看做一个包装函数的函数。
二、无参装饰器
最常见、最简单的装饰器形式,在不传递任何参数的情况下,为函数动态添加功能,如:日志记录、性能测试、权限校验、事务管理等
1、无参装饰器的模板
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
2.案例
def my_decorator(func): # 第一层:接收“被装饰的函数”
@functools.wraps(func) # 将函数的属性与原函数保持一致 如 __doc__ __name__等
def wrapper(): # 第二层:代替原函数执行,添加新功能
# 在函数调用前执行的代码
print("函数被调用了!")
# 调用原函数
result = func()
# 在函数调用后执行的代码
print("函数执行完毕!")
return result
return wrapper # 返回内部函数,等待执行
@my_decorator
def my_function():
print("我是原函数!")
# 调用被装饰后的函数
my_function()
3.关键点
@my_decorator 只是 语法糖,它等价于 my_function = my_decorator(my_function)。
装饰器在函数定义时立即执行,而内部的 wrapper 函数在函数被调用时才执行。
为了保留原函数的元信息(如名字、文档字符串),通常会用 @functools.wraps(func) 来装饰 wrapper 函数。
三、有参装饰器
当想通过参数来控制装饰器的行为时,就需要使用有参装饰器。让装饰器本身可以接收参数,从而实现更灵活、可配置的功能封装。例如,为路由指定URL,为权限检查指定角色等。一个有参装饰器需要三层嵌套函数来实现:
1.有参装饰器的模板:
def outter2(x,y,z,a,b):
def outter1(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter1
2.案例
import functools
def repeat(num_times): # 第一层:接收“装饰器的参数”
def decorator_repeat(func): # 第二层:接收“被装饰的函数”
@functools.wraps(func)
def wrapper(*args, **kwargs): # 第三层:代替原函数执行,添加新功能
print(f"我将重复执行 {num_times} 次")
for _ in range(num_times):
result = func(*args, **kwargs)
return result # 通常返回最后一次调用的结果
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
3.关键点
@repeat(num_times=3) 实际上做了三件事:
调用 repeat(3),返回 decorator_repeat 函数。
将 greet 函数传递给 decorator_repeat,即 decorator_repeat(greet)。
最终 decorator_repeat(greet) 返回 wrapper 函数,并赋值给 greet。
最外层的参数 (num_times) 在装饰器被定义时就确定了,它决定了内部装饰行为的具体模式。
四、多层装饰器
多重装饰器的应用:当要求每一个代码块需要两个或多个检查时,这样就需要两个或多个装饰器对此代码块进行监督。下面给出一个使用装饰器同时对代码进行登录和记录执行时间:
1 加载顺序:自下而上
2 执行顺序:自上而下运行内层的wrapper函数
3.案例
import time
def deco1(func1):
def wrapper1(*args,**kwargs):
start_time = time.time()
res1=func1(*args,**kwargs)
stop_time = time.time()
print(stop_time - start_time)
return res1
return wrapper1
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
return wrapper2
@deco2 #deco2先执行
@deco1
def index(x,y):
time.sleep(1)
print('index=>',x,y)
index(1,2)
"一劳永逸" 的话,有是有的,而 "一劳永逸" 的事却极少

浙公网安备 33010602011771号