一:什么是装饰器
器:指的是工具,可以定义为函数
装饰:指的是为其他事物添加额外的东西点缀
装饰器:指的是定义一个函数,该函数是用来给其他函数添加额外的功能
二、为什么要用装饰器
开放封闭原则:
开放:指的是对拓展功能是开放的
封闭:指的是对修改源代码是封闭的
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
装饰器就是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加新功能。
三、装饰器的实现
函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。
3.1:无参装饰器的实现
无参装饰器的模版
def outer(func): def wrapper(): res = func() return res return wrapper @outer # 加在被装饰对象的上方
@outer # 加在被装饰对象的上方
如果想为下述函数添加统计其执行时间的功能
import time def get_time(): time.sleep(3) print("welcome") return 200 get_time()
使用装饰器:
import time def outer(func): def wrapper(): stat_time = time.time() res = func() end_time = time.time() print(f'程序运行的时间为{end_time-stat_time}') return res return wrapper @outer def get_time(): time.sleep(3) print("welcome") return 200 get_time()
输出结果:
welcome
程序运行的时间为3.0009207725524902
3.2:有参装饰器的实现
有参装饰器的模版
def outer(func): def wrapper(*args, **kwargs): res = func(*args, **kwargs) return res return wrapper @outer
@outer # 加在被装饰对象的上方
如果想为下述函数添加统计其执行时间的功能
import time def time_demo(a, b): time.sleep(3) print("welcome") print(a+b) time_demo(3, 5)
使用装饰器:
import time def outer(func): def wrapper(a, b): stat_time = time.time() res = func(a, b) end_time = time.time() print(f'程序运行的时间为{end_time-stat_time}') return res return wrapper @outer def time_demo(a, b): time.sleep(3) print("welcome") print(a+b) time_demo(3, 5)
输出结果:
welcome
8
程序运行的时间为3.0009207725524902
3.3:装饰器函数本身带有参数
@logging(level='INFO')它就是一个函数,会被立刻执行,返回结果是一个装饰器。
与上面的例子相同:
import time def loglevel(level): def outer(func): def wrapper(a, b): stat_time = time.time() res = func(a, b) end_time = time.time() print(f'程序运行的时间为{end_time-stat_time}') if level == 'INFO': print("level is INFO") return res return wrapper return outer @loglevel(level='INFO') # 相当于:time_demo=loglevel("INFO")(time_demo), -> loglevel("INFO") = outer, 因此time_demo=outer(time_demo) def time_demo(a, b): time.sleep(3) print("welcome") print(a+b) time_demo(3, 5)
@loglevel(level='INFO'):先执行loglevel(level='INFO'),得到outer返回值后,再执行@outer
输出结果:
welcome
8
程序运行的时间为3.0051047801971436
level is INFO
3.4:叠加多个装饰器
@deco3 @deco2 @deco1 def index(): pass
执行顺序是:从最上面的装饰器依次往下执行,将下面的作为参数传入
index=deco3(deco2(deco1(index)))
装饰器
def home(): print("---首页----") def america(): print("----欧美专区----") def japan(): print("----日韩专区----") def henan(): print("----河南专区----")
假设这是一个网站的各个专区的页面,调用函数即为打开页面,现在要添加一个用户认证的功能
account = { "is_authenticated":False,# 用户登录了就把这个改成True "username":"alex", # 假装这是DB里存的用户信息 "password":"abc123" # 假装这是DB里存的用户信息 } def login(): if account["is_authenticated"] is False: username = input("user:") password = input("pasword:") if username == account["username"] and password == account["password"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password!") else: print("用户已登录,验证通过...") def home(): print("---首页----") def america(): login() # 执行前加上验证 print("----欧美专区----") def japan(): print("----日韩专区----") def henan(): login() # 执行前加上验证 print("----河南专区----") home() america() henan()
你可以加一个关于用户认证的函数,然后在每一个页面打开前都调用这个函数。
但是这样虽然程序的功能实现了,但是违反了“开放-封闭”原则。
-
封闭:已实现的功能代码块不应该被修改
-
开放:对现有功能的扩展开放
这样修改代码相当于是对已有的功能的代码块进行了修改,是不符合开放封闭原则的,所以要找一种不修改原来代码的方式来增加这个功能。
account = { "is_authenticated":False,# 用户登录了就把这个改成True "username":"alex", # 假装这是DB里存的用户信息 "password":"abc123" # 假装这是DB里存的用户信息 } def login(func): if account["is_authenticated"] is False: username = input("user:") password = input("pasword:") if username == account["username"] and password == account["password"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password!") if account["is_authenticated"] is True: # 主要改了这 func() # 认证成功了就执行传入进来的函数
def home(): print("---首页----") def america(): print("----欧美专区----") def japan(): print("----日韩专区----") def henan(): print("----河南专区----")
home() login(america) # 需要验证就调用 login,把需要验证的功能 当做一个参数传给login login(henan)
通过之前学过的高阶函数,在不修改源代码的情况下,实现了认证功能的添加,但是这种方式仍然不好,因为修改了原来程序的调用方式,修改以后,所有的用户都要修改调用方式,显然是不合理的。
那么如何在不修改源代码和原来调用方式的情况下给函数添加功能?
account = { "is_authenticated":False,# 用户登录了就把这个改成True "username":"alex", # 假装这是DB里存的用户信息 "password":"abc123" # 假装这是DB里存的用户信息 } def login(func): def inner(): # 再定义一层函数 if account["is_authenticated"] is False: username = input("user:") password = input("pasword:") if username == account["username"] and password == account["password"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password!") if account["is_authenticated"] is True: func() return inner # 注意这里只返回inner的内存地址,不执行
america() # 相当于执行inner() henan()
上面的代码将原函数名america传给login()函数,login()函又定义了一层函数inner,并且返回了内部函数的函数名inner,然后用america进行函数名的替换,这样再次执行america()时,相当于执行的就是
inner()函数。这样就实现了在没有修改源代码和调用方式的情况下给原函数增加功能的目的。
def america(): print("----欧美专区----") def henan(): print("----河南专区----") home() america = login(america) henan = login(henan) # 通常装饰器使用这种写法 @login # 相当于america = login(america) def america(): print("----欧美专区----") @login # 相当于henan = login(henan) def henan(): print("----河南专区----")
当需要给原函数传递参数时,可以使用非固定参数,即给innder(*args,**kwargs),然后给func(*args,**kwargs) 就可以实现了。
- #装饰器函数在被装饰函数定义好后立即执行。多个装饰器的调用顺序是自下往上的。
- # 被装饰函数执行时,装饰器的执行顺序是从上往下的。
执行顺序如上。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_48323589/article/details/130189887