装饰器

前言
装饰器就是类似于女孩子的发卡。你喜欢的一个女孩子,她可以有很多个发卡,而当她戴上不同的发卡,她的头顶上就是装饰了不同的发卡。但是你喜欢的女孩子还是你喜欢的女孩子。如果还觉得不理解的话,装饰器就是咱们的手机壳,你尽管套上了手机壳,但并不影响你的手机功能,可你的手机还是该可以给你玩,该打电话打电话,该玩游戏玩游戏。而你的手机就变成了带手机壳的手机。
什么是装饰器
| 装饰器,顾名思义就是装饰XXX的工具。在python中,装饰器的本质就是一个高阶函数,它接受一个函数作为参数,并返回一个被装饰后的函数。 |
| 装饰器的作用如下: |
| --在不修改被装饰函数的源代码和调用方式的情况下,给被装饰函数添加额外的功能。 |
| --即就是你传一个函数给装饰器,装饰器不会改变该函数的代码和调用方式就能使该函数获得额外的功能。 |
为什么要使用装饰器
| --装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。 |
| 装饰器的使用符合了面向对象编程的开放封闭原则。 |
| |
| --开放封闭原则主要体现在两个方面: |
| 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。 |
| 对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类尽任何修改。 |
装饰器的实现
实现原理
| 函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是'函数嵌套+闭包+函数对象'的组合使用的产物。 |
实现无参装饰器
无参装饰器模板
| |
| def wrapper(func): |
| def inner(): |
| |
| '''...''' |
| |
| result = func() |
| |
| '''...''' |
| return result |
| return inner |
一般写法
| |
| import time |
| |
| def how_much_time(func): |
| def inner(): |
| t_start = time.time() |
| func() |
| t_end = time.time() |
| print("一共花费了{0}秒时间".format(t_end - t_start, )) |
| return inner |
| |
| |
| def sleep_5s(): |
| time.sleep(5) |
| print("%d秒结束了" % (5,)) |
| |
| def sleep_6s(): |
| time.sleep(6) |
| print("%d秒结束了" % (6,)) |
| |
| sleep_5s = how_much_time(sleep_5s) |
| |
| sleep_6s = how_much_time(sleep_6s) |
| |
| t1 = threading.Thread(target=sleep_5s) |
| t2 = threading.Thread(target=sleep_6s) |
| t1.start() |
| t2.start() |
| |
| |
| |
| |
| |
| import time |
| import threading |
| |
| def how_much_time(func): |
| def inner(): |
| t_start = time.time() |
| func() |
| t_end = time.time() |
| print("一共花费了{0}秒时间".format(t_end - t_start, )) |
| |
| return inner |
| |
| |
| @how_much_time |
| |
| def sleep_5s(): |
| time.sleep(5) |
| print("%d秒结束了" % (5,)) |
| |
| @how_much_time |
| def sleep_6s(): |
| time.sleep(6) |
| print("%d秒结束了" % (6,)) |
| |
| t1 = threading.Thread(target=sleep_5s) |
| t2 = threading.Thread(target=sleep_6s) |
| t1.start() |
| t2.start() |
| |
| |
| |
| |
补充:给一个函数添加两个装饰器
| |
| import time |
| import threading |
| |
| def how_much_time(func): |
| print("how_much_time函数开始了") |
| def inner(): |
| t_start = time.time() |
| func() |
| t_end = time.time() |
| print("一共花费了{0}秒时间".format(t_end - t_start, )) |
| return inner |
| |
| def mylog(func): |
| print("mylog函数开始了") |
| def inner_1(): |
| print("start") |
| func() |
| print("end") |
| return inner_1 |
| |
| @mylog |
| @how_much_time |
| |
| def sleep_5s(): |
| time.sleep(5) |
| print("%d秒结束了" % (5,)) |
| |
| if __name__ == '__main__': |
| sleep_5s() |
| |
| |
| |
| |
| |
| |
| 当一个函数具有两个装饰器时的执行顺序是: |
| 1,第一步先执行how_much_time函数的外部代码; |
| 2,第二步执行mylog函数的外部代码; |
| 3,第三步执行mylog的内部函数代码; |
| 4,第四步执行how_much_time函数的内部函数$代码 |
实现有参装饰器
了解无参装饰器的实现原理后,我们可以再实现一个用来为被装饰对象添加认证功能的装饰器,实现的基本形式如下:
| def deco(func): |
| def wrapper(*args,**kwargs): |
| 编写基于文件的认证,认证通过则执行res=func(*args,**kwargs),并返回res |
| return wrapper |
如果我们想提供多种不同的认证方式以供选择,单从wrapper函数的实现角度改写如下
| def deco(func): |
| def wrapper(*args,**kwargs): |
| if driver == 'file': |
| 编写基于文件的认证,认证通过则执行res=func(*args,**kwargs),并返回res |
| elif driver == 'mysql': |
| 编写基于mysql认证,认证通过则执行res=func(*args,**kwargs),并返回res |
| return wrapper |
函数wrapper需要一个driver参数,而函数deco与wrapper的参数都有其特定的功能,不能用来接受其他类别的参数,可以在deco的外部再包一层函数auth,用来专门接受额外的参数,这样便保证了在auth函数内无论多少层都可以引用到
| def auth(driver): |
| def deco(func): |
| …… |
| return deco |
此时我们就实现了一个有参装饰器,使用方式如下
| 先调用auth_type(driver='file'),得到@deco,deco是一个闭包函数, |
| 包含了对外部作用域名字driver的引用,@deco的语法意义与无参装饰器一样 |
| @auth(driver='file') |
| def index(): |
| pass |
| @auth(driver='mysql') |
| def home(): |
| pass |
可以使用help(函数名)来查看函数的文档注释,本质就是查看函数的doc属性,但对于被装饰之后的函数,查看文档注释
| @timer |
| def home(name): |
| ''' |
| home page function |
| :param name: str |
| :return: None |
| ''' |
| time.sleep(5) |
| print('Welcome to the home page',name) |
| |
| print(help(home)) |
| ''' |
| 打印结果: |
| |
| Help on function wrapper in module __main__: |
| |
| wrapper(*args **kwargs) |
| |
| None |
| |
在被装饰之后home=wrapper,查看home.name也可以发现home的函数名确实是wrapper,想要保留原函数的文档和函数名属性,需要修正装饰器
| def timer(func): |
| def wrapper(*args,**kwargs): |
| start_time=time.time() |
| res=func(*args,**kwargs) |
| stop_time=time.time() |
| print('run time is %s' %(stop_time-start_time)) |
| return res |
| wrapper.__doc__=func.__doc__ |
| wrapper.__name__=func.__name__ |
| return wrapper |
按照上述方式来实现保留原函数属性过于麻烦,functools模块下提供一个装饰器wraps专门用来帮我们实现这件事,用法如下
| from functools import wraps |
| |
| def timer(func): |
| @wraps(func) |
| def wrapper(*args,**kwargs): |
| start_time=time.time() |
| res=func(*args,**kwargs) |
| stop_time=time.time() |
| print('run time is %s' %(stop_time-start_time)) |
| return res |
| return wrapper |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏