python装饰器
特征
器:指的是工具。
装饰:给被装饰对象添加额外的功能。
原则
开放封闭原则
开放:对扩展开放。
封闭:对修改封闭。
核心思想
在不改变被"装饰对象内部代码"和"原有调用方式"的基础之上添加额外功能。
在满足上述条件的情况下添加计算运行时间功能。
装饰器简易版本
装饰器的本来需求是要不改变原有的调用方式,因此可以使用将装饰器的函数名赋给和原来函数名相同的新变量名,来达成偷梁换柱的目的。
此时的index调用的其实是get_time2函数。
解决返回值问题
在解决了参数问题后,还有返回值的问题,当原函数有返回值时,上述的程序是无法返回的。因此还要添加返回值。
需要在装饰器内定义一个变量来接收原函数的返回值,然后在返回该变量。
认证装饰器
通过装饰器可以添加一个认证功能,利用全局变量,可以达成只用登录一次就使用全部功能d效果。
is_login = {'is_login': False} def login(func): def login1(*args, **kwargs): if is_login.get('is_login'): res = func(*args, **kwargs) return res username = input('请输入用户名:') password = input('请输入密码:') if username == 'tom' and password == '123': is_login['is_login'] = True res = func(*args, **kwargs) return res else: print('用户名或密码错误') return login1 def index(): print('登录成功') def index1(): print('第二个功能') index = login(index) index1 = login(index1) index() index1()
效果为当第一次输入正确后,就不需要再登录,当第一登录失败后则需在此登录。
装饰器模板
通过上述的例子,可以推导出装饰器的一个万用模板。
利用该模板可以轻松完成装饰器。
def outer(func): def inner(*args, **kwargs): print('执行函数之前可以添加的额外功能') res = func(*args, **kwargs) # 执行被装饰的函数 print('执行函数之后可以添加的额外功能') return res # 将被装饰函数执行之后的返回值返回 return inner
装饰器语法糖
在完成装饰器后,可以使用装饰器语法糖(@函数名)来代替index = login(index)这句话,来使装饰器的调用更接近原函数。
语法糖的语法规范:紧贴在需要装饰的函数上方。
语法糖的内部原理:将紧贴的函数名作为参数传给装饰器函数调用。
import time def get_time1(func): def get_time2(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间%s' % (end_time - start_time)) return res return get_time2 @get_time1 def index(name): time.sleep(1) print('%s在测量时间' % name) return 'time' print(index('tom'))
语法糖可以 多个叠加,当函数上方两个以上的语法糖时,他相当于是把下方的函数作为参数依次向上传,在运行时则是从上向下运行。
import time is_login = {'is_login': False} def get_time1(func): def get_time2(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间%s' % (end_time - start_time)) return res return get_time2 def login1(func): def login2(*args, **kwargs): if is_login.get('is_login'): res = func(*args, **kwargs) return res username = input('请输入用户名:') password = input('请输入密码:') if username == 'tom' and password == '123': res = func(*args, **kwargs) return res else: print('用户名或密码错误') return login2 @login1 @get_time1 def index(name): time.sleep(1) print('%s在测量时间' % name) return 'time' index('tom')
因为要求登录的语法糖在上方,所以会先进行登录在进行计时。结果如下:
装饰器修复技术
我们现在使用了装饰器虽然看似调用的函数是原函数,但其实是其他的函数。为了使装饰器变得更像真的,可以使用装饰器修复技术。
他的作用是在使用装饰器后对函数进行输出,所看到的依旧是原函数的内存地址。
有参装饰器
在函数的模板中可以看到,双层的函数参数都是被固定的,有他们自己的功能。当我们还想往装饰器里传入参数时,就会遇到问题。
为解决这个问题,所采取的方法是在外层再包一层函数,将参数传入这层来供内层的装饰器使用。
此时需要的参数可以通过装饰器的语法糖来传入。
def more(another): from functools import wraps def get_time1(func): @wraps(func) def get_time2(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间%s' % (end_time - start_time)) print('需要一个外层参数%s' % another) return res return get_time2 return get_time1 @more('示例') def index(name): time.sleep(1) print('%s在测量时间' % name) return 'time' index('tom')
运行结果如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通