Python无参装饰器实现原理与优化步骤详解
本文详细解析Python无参装饰器的实现过程,从基础概念出发,通过三种方案逐步优化,解决参数传递、返回值保留及代码冗余问题,最终展示标准模板与@语法糖的使用。
一、什么是装饰器
装饰器本质上是一个函数,其核心作用是为其他函数动态添加额外的功能。
二、何时用装饰器
当需要在不修改被装饰对象源代码及其调用方式的前提下,为其添加额外功能时,应使用装饰器。
三、如何写一个装饰器
假设我们有一个名为help的函数,其中包含time.sleep()用于模拟执行耗时,并通过print打印传入参数以便分析。若需为该函数添加运行时间统计功能,可尝试以下方案。
import time
def help(x, y):
time.sleep(1)
print(f'x={x} y={y}')
help(1, 2)
方案一:
在help函数的开始和结束处分别调用time.time(),计算两者的差值即为运行时间。
import time
def help(x, y):
start = time.time()
time.sleep(1)
print(f'x={x} y={y}')
stop = time.time()
print(stop - start)
help(1, 2)
运行结果:

方案一虽然在help中增加了代码,但未改变其调用方式,却修改了源代码。接下来探讨如何在保持源代码和调用方式均不变的情况下实现目标。
既然函数内部不能修改,我们可以尝试在函数外部添加相关代码,即在help调用前后插入逻辑,这便是方案二。
方案二:
import time
def help(x, y):
time.sleep(1)
print(f'x={x} y={y}')
start = time.time()
help(1, 2)
stop = time.time()
print(stop - start)
运行结果:

虽然方案二看似可行,但多次调用help函数时,需在每次调用前后重复添加相同代码,导致冗余。既然这些前后代码固定不变,可将其封装为独立函数。
方案三:
import time
def help(x, y):
time.sleep(1)
print(f'x={x} y={y}')
def wrapper():
start = time.time()
help(1, 2)
stop = time.time()
print(stop - start)
wrapper()
运行一下:

此方案解决了多次调用的冗余问题,但改变了help的调用方式,且help参数固定,仅能修饰特定函数。接下来逐步优化。
优化一(参数优化,实现任意参数):
为实现参数灵活传递,可将help的实参通过wrapper传入。为支持任意参数,使用*args和**kwargs作为wrapper的参数,改进方案三如下(为help增加额外参数以便分析):
import time
def help(x, y, z):
time.sleep(1)
print(f'x={x} y={y} z={z}')
def wrapper(*args, **kwargs):
start = time.time()
help(*args, **kwargs)
stop = time.time()
print(stop - start)
wrapper(1, 2, 3)
运行一下:

此时help的参数变得灵活,继续优化。
优化二(实现装饰其他对象):
若要装饰其他对象,help位置需接受可变参数,即用户输入的参数,因此wrapper函数应调整为:
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
stop = time.time()
print(stop - start)
然而,期望wrapper与内部调用的func函数参数一致,即不改变wrapper的参数形式,那么func的值应如何传入?
可通过闭包函数传参,修改如下:
def outter(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
stop = time.time()
print(stop - start)
return wrapper
此时装饰其他函数时,只需将函数名作为outter函数的参数传入:
import time
def help(x, y, z):
time.sleep(1)
print(f'这是help的{x}{y}{z}')
def others(x, y, z):
time.sleep(1)
print(f'这是others的{x}{y}{z}')
def outter(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
stop = time.time()
print(stop - start)
return wrapper
help = outter(help)
others = outter(others)
help(1, 2, 3)
others(4, 5, 6)
运行一下:

结果符合预期。由于outter内的func位于局部名称空间,而outter外的func位于全局名称空间,调用时互不冲突,且代码可读性较高,似乎达成初始目标。但需进一步思考:若函数有返回值,修饰后能否获取原函数的返回值?
优化三(得到相同返回值):
回到wrapper函数,若需获取func的返回值,可直接将其赋值给res,再return res:
import time
def help(x, y, z):
time.sleep(1)
print(f'这是help的{x}{y}{z}')
return 'help'
def others(x, y, z):
time.sleep(1)
print(f'这是others的{x}{y}{z}')
return 'others'
def outter(func):
def wrapper(*args, **kwargs):
start = time.time()
res=func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
help = outter(help)
others = outter(others)
res1=help(1, 2, 3)
res2=others(4, 5, 6)
print(res1,res2)
没毛病,跑一下:

总结:
至此,我们完成了一个简单的无参装饰器,装饰后的func既未改变源代码,也未改变调用方式。
但代码略显冗余,Python语法规定:在被装饰对象正上方单独一行写@装饰器名字,等价于func=outter(func),从而简化代码。无参装饰器的标准模板如下:
def outter(func):
def wrapper(*args,**kwargs):
# 1、调用原函数
# 2、增加的新功能
res=func(*args,**kwargs)
return res
return wrapper
#使用时
@outter
def func:
pass
到此这篇关于Python无参装饰器的文章就介绍到这了,更多相关Python无参装饰器内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













