第三篇 装饰器
装饰器
装饰器介绍
-
器:指的是工具
-
装饰:给被装饰对象添加额外的功能
装饰器就是用来为被装饰对象添加额外功能的工具
装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
装饰器的原则
在添加新功能的时候要遵循开放封闭的原则:软件上线运行之后,应该对扩展功能开放,对修改源代码封闭
-
原则:
-
1、不修改被装饰函数内的源代码
-
2、不修改函数原有的调用方式
-
装饰器的核心思想就是在遵循原则1和2的基础之上,为被装饰对象添加额外功能
装饰器的实现
函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,装饰器并不是一门新技术,而是由“名称空间+函数对象+闭包函数”组合使用的产物
# 需求:在不修改index函数的源代码和调用方式的前提下为其添加统计运行时间的新功能 import time # 导入时间模块 def index(): # 一个函数 time.sleep(1) # 模拟index运行的秒数 print('from index') index() # 调用方式 # 方案一:失败 # 问题:确实添加了新功能,但是修改了源代码 import time def index(): start = time.time() # 统计开始时间 time.sleep(1) print('from index') stop = time.time() # 统计结束时间 print('运行时间为: %s' %(stop - start)) # 计算差值 index() # 方案二:失败 # 问题:看似装饰器的效果实现了,但是引发了重复代码的问题 import time def index(): time.sleep(1) print('from index') start = time.time() index() stop = time.time() print('运行时间为: %s' %(stop - start)) # 方案三:失败 # 问题:把index函数名变成变量名func,要为get_time函数的函数体代码传参数, # 直接用参数传参,装饰对象虽然写活了,但只能装饰index import time def index(): time.sleep(1) print('from index') def get_time(func): start = time.time() func() # 被装饰函数 stop = time.time() print('运行时间为: %s' %(stop - start)) wrapper(index) # 以上三种方案都失败了,那么往下看
装饰器简易版本
# 给函数添加统计执行时间的功能,解决上述问题 import time def index(): # index = 被装饰对象index函数的内存地址 time.sleep(3) print('from index') def outer(func): # func = 被装饰对象index函数的内存地址 def get_time(): start = time.time() func() # 被装饰对象index函数的内存地址 stop = time.time() print('运行时间为: %s' %(stop - start)) return get_time # 将get_time函数名返回出去 # outer-》返回get_time函数的内存地址-》index = get_time函数的内存地址 index = outer(index) # outer=(被装饰对象index函数的内存地址) index() ''' from index 运行时间为: 3.0035877227783203 '''
装饰器解决参数问题
import time def index(): time.sleep(1) print('两情若是久长时,又岂在公公母母!!!') def home(name): # home = 被装饰对象home函数的内存地址 time.sleep(2) print("欢迎 %s 来到主页" % name) def outer(func): # func = 被装饰对象home函数的内存地址 # func = home def get_time(*args, **kwargs): start = time.time() func(*args, **kwargs) # 被装饰对象home函数的内存地址 stop = time.time() print('运行时间为: %s' % (stop - start)) return get_time # 将get_time函数名返回出去 home = outer(home) # 偷梁换柱:home = get_time函数的内存地址 home('jason') # get_time('jason',) index = outer(index) index() # get_time() ''' 欢迎 jason 来到主页 运行时间为: 2.0049831867218018 两情若是久长时,又岂在公公母母!!! 运行时间为: 1.0011849403381348 '''
装饰器解决返回值问题
import time def index(): time.sleep(1) print('两情若是久长时,又岂在公公母母!!!') return 'from index' def home(name): # home = 被装饰对象home函数的内存地址 time.sleep(2) print("欢迎 %s 来到主页" % name) return 'home index' def outer(func): # func = 被装饰对象home函数的内存地址 # func = home def get_time(*args, **kwargs): start = time.time() res = func(*args, **kwargs) stop = time.time() print('运行时间为: %s' % (stop - start)) return res # 执行完get_time之后返回被装饰函数执行之后的返回值 return get_time # 将get_time函数名返回出去 index = outer(index) # index = get_time函数的内存地址 res = index() # get_time() print(res) home = outer(home) # home = get_time函数的内存地址 res1 = home('jason') # get_time('jason',) print(res1) ''' 两情若是久长时,又岂在公公母母!!! 运行时间为: 1.0042109489440918 from index 欢迎 jason 来到主页 运行时间为: 2.0018317699432373 home index '''
认证装饰器
# 访问所有的界面都需要认证,一次认证通过所有界面都可以正常访问 import time # 全局标志位,记录用户的登录状态 is_login = {'is_login': False} def index(): time.sleep(1) print('欢迎来到index界面!!!') def home(): time.sleep(1) print('欢迎来到home界面!!!') def register(): time.sleep(1) print('欢迎来到登录界面!!!') # 编写登录认证装饰器 def login_auth(func): def auth(*args, **kwargs): # 每次调用都会先判断用户是否已经登录 if is_login.get('is_login'): res = func(*args, **kwargs) return res username = input('请输入用户名>>>:').strip() password = input('请输入密码>>>:').strip() if username == 'jason' and password == '123': res = func(*args, **kwargs) # 正常执行函数index is_login['is_login'] = True # 将记录用户登录状态的数据修改 return res else: print('用户名或密码输入错误!!!') return auth """ 在调用index之前需要用户输入用户名和密码认证 正确才可以调用,错误直接拒绝,执行下一个需要认证的函数 """ index = login_auth(index) index() home = login_auth(home) home() register = login_auth(register) register() """ 请输入用户名>>>:jason 请输入密码>>>:123 欢迎来到index界面!!! 欢迎来到home界面!!! 欢迎来到登录界面!!! """
无参装饰器固定模板
def outer(func): def inner(*args,**kwargs): # inner,在inner函数内写被装饰的函数 print('执行被装饰函数之前可以添加的额外功能') res = func(*args,**kwargs) # 执行被装饰的函数 print('执行被装饰函数之后可以添加的额外功能') return res # # 将被装饰函数执行之后的返回值返回 return inner """ func(*args,**kwargs) # *args,**keargs让参数保持一致,装的像一点 return res # 让返回值保持一致,装的像一点 """ # 以上的装饰器为被装饰对象啥新功能也没添加,就是无参装饰器模板
装饰器语法糖
语法糖:让你开心的语法
-
装饰器语法糖书写规范
- 语法糖必须紧贴在被装饰对象的上方
-
装饰器语法糖内部原理
- 会自动将紧贴着的被装饰对象名字当做参数加括号传给装饰器函数自动调用
- 如果上方没有其他语法糖则直接使用与被装饰对象相同的变量名接收装饰器的返回值
def outer(func): # func = 被装饰对象index函数的内存地址 def inner(*args, **kwargs): print('执行函数之前可以添加的额外功能') res = func(*args, **kwargs) # 执行被装饰的函数 print('执行函数之后可以添加的额外功能') return res # 将被装饰函数执行之后的返回值返回 return inner # 在被装饰对象正上方的单独一行写@装饰器名字 @outer # index = outer(index) def index(*args, **kwargs): print('from index') @outer # home = outer(home) def home(): print('from home') """ @outer index = outter(index) outer(被装饰对象index函数的内存地址) -> 返回inner函数 的内存地址-> index = inner函数的内存地址 """
双层语法糖
# 统计函数运行时间装饰器 import time def time_filter(func): def get_time(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('函数的运行时间为:%s' % (stop_time - start_time)) return res return get_time # 校验用户登录装饰装饰器 is_login = {'is_login': False} # 全局标志位 def login_auth(func): def auth(*args, **kwargs): # 判断用户是否登录 if is_login.get('is_login'): res = func(*args, **kwargs) return res # 先获取用户的用户名和密码 username = input('请输入用户名>>>:').strip() password = input('请输入密码>>>:').strip() # 校验用户名和密码是否正确 if username == 'jason' and password == '123': print('登录成功!!!') res = func(*args, **kwargs) is_login['is_login'] = True return res else: print('用户名或密码错误,无权限执行!!!') return auth @login_auth @time_filter def index(res): time.sleep(2) print('我是index'.center(30)) return '函数1>>>: %s' % res print(index('ok')) @login_auth @time_filter def home(res): time.sleep(2) print('我是home'.center(30)) return '函数2>>>: %s' % res print(home('no')) ''' 请输入用户名>>>:jason 请输入密码>>>:123 登录成功!!! 我是index 函数的运行时间为:2.004554033279419 函数1>>>: ok 我是home 函数的运行时间为:2.003156900405884 函数2>>>: no '''
叠加多个装饰器
def decorate1(func1): # func1 = inner2的内存地址 print('加载了 decorate1') def inner1(*args, **kwargs): print('执行了 inner1') res1 = func1(*args, **kwargs) return res1 return inner1 def decorate2(func2): # func2 = inner3的内存地址 print('加载了 decorate2') def inner2(*args, **kwargs): print('执行了 inner2') res2 = func2(*args, **kwargs) return res2 return inner2 def decorate3(func3): # func3 = 被装饰index的内存地址 print('加载了 decorate3') def inner3(*args, **kwargs): print('执行了 inner3') res3 = func3(*args, **kwargs) return res3 return inner3 # index = inner1的内存地址 @decorate1 # decorate1(inner2的内存地址) 返回 inner1的内存地址 @decorate2 # decorate2(inner3的内存地址) 返回 inner2的内存地址 @decorate3 # decorate3(被装饰index的内存地址) 返回 inner3的内存地址 def index(): print('我是 index') index() # 语法糖由下往上自动执行
装饰器修复技术
'''如何做到无法轻易看出是否被装饰''' from functools import wraps # 固定句式 def outer(func): @wraps(func) # 修复技术就是为了让被装饰对象更加不容易被察觉装饰了 def inner(*args, **kwargs): print('执行被装饰函数之前可以添加的额外功能') res = func(*args, **kwargs) # 执行被装饰的函数 print('执行被装饰函数之后可以添加的额外功能') return res # 将被装饰函数执行之后的返回值返回 return inner @outer # index = outer(index) def index(): print('from index') print(index) help(index) def home(): """这是一个home函数""" print('from home') help(index) # help() 可以查看函数的基本信息(函数名 函数注释) # help(home) # print(index) # help(len)
有参装饰器
# 如果装饰器内部还需要额外的参数,就需要用到有参装饰器 def outer(source_data): # source_data = 'file' def login_auth(func): def auth(*args,**kwargs): # 2.校验用户名和密码是否正确 # 数据的校验方式可以切换多种 if source_data == 'file': # 从文件中获取用户数据并比对 print('file文件获取') elif source_data == 'MySQL': # 从MySQL数据库中获取数据比对 print('MySQL数据库获取') elif source_data == 'postgreSQL': # 从postgreSQL数据库中获取数据对比 print('postgreSQL数据库获取') else: print('用户名或密码错误 无法执行函数') return auth return login_auth @outer('file') def index(): print('from index') @outer('MySQL') def home(): print('from home') index() home()
分类:
python 基础
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!