装饰器
- 装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能
- 装饰器经常用于有切面需求的场景
- 插入日志、性能测试、事务处理、缓存、权限校验等应用场景
- 有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用
【一】装饰器的作用
- 软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。
- 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
- 对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
- 软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃
- 而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。
【二】装饰器的分类
- 函数装饰器分为:无参装饰器和有参装饰器
- 二者的实现原理一样,都是
函数嵌套+闭包+函数对象
的组合使用的产物
【三】无参装饰器
| login_user_dict = {'username': 'heart', 'is_admin': True} |
| user_data_dict = {'heart': {'password': '123', 'role': 'admin'}, |
| 'god': {'password': '321', 'role': 'normal'}} |
| |
| def login(username, password): |
| if password == user_data_dict[username].get('password'): |
| print(f'登录成功!') |
| login_user_dict['username'] = username |
| if user_data_dict[username].get('role') == 'admin': |
| login_user_dict['is_admin'] = True |
| else: |
| login_user_dict['is_admin'] = False |
| |
| |
| def check_admin(func): |
| def inner(): |
| |
| if login_user_dict['username'] and login_user_dict['is_admin']: |
| |
| res = func() |
| return res |
| |
| else: |
| |
| return False, '重新登录' |
| return inner |
| |
| @check_admin |
| def get_money(): |
| |
| return True, '取了一万块' |
| print(get_money()) |
(1)无参装饰器模板
| def outer(func): |
| def inner(*args,**kwargs): |
| |
| res = func(*args,**kwargs) |
| |
| return res |
| |
| return inner |
| @outer |
| def add(*args,**kwargs): |
| return 1 |
| print(add()) |
(2)看电影练习
| |
| |
| |
| |
| |
| user_data = {'heart': 20} |
| |
| def outer(func): |
| def inner(*args,**kwargs): |
| if user_data['heart']>=18: |
| res=func() |
| return res |
| else: |
| return False,'18禁!' |
| return inner |
| |
| def watch(*args,**kwargs): |
| return True,'看电影!' |
| |
| watch=outer(watch) |
| print(watch()) |
| |
| |
| @outer |
| def watch(*args,**kwargs): |
| return True,'看电影!' |
| print(watch()) |
【四】有参装饰器
- 本质上就是在无参装饰器外面再套一层函数,再判断他的tag条件
- 下面是取钱函数示例
| user_data = {'username': 'heart', 'password': '123'} |
| bank_data = {'heart': {'pay_pwd': '1234', 'balance': 1000}} |
| |
| |
| def uuu(tag=None, tag_func=None): |
| if tag == 'login': |
| def outer(func): |
| def inner(*args, **kwargs): |
| username = input('请输入用户名:>>>').strip() |
| password = input('请输入密码:>>>').strip() |
| if username != user_data['username'] or password != user_data['password']: |
| return False, '用户名或密码错误!' |
| else: |
| return func(username=username, password=password, *args, **kwargs) |
| |
| return inner |
| |
| return outer |
| elif tag == 'check': |
| def check_balance(func): |
| def inner(*args, **kwargs): |
| balance = input('请输入金额:>>>').strip() |
| if not balance.isdigit(): |
| return False, '非法' |
| if tag_func == 'add': |
| pay_password = input('请输入密码:>>>').strip() |
| if pay_password != bank_data['heart']['pay_pwd']: |
| return False, '密码错误' |
| balance = int(balance) |
| else: |
| pay_password = input('请输入密码:>>>').strip() |
| if pay_password != bank_data['heart']['pay_pwd']: |
| return False, '密码错误' |
| if not balance.isdigit(): |
| return False, '非法' |
| balance = int(balance) |
| if balance > bank_data['heart']['balance']: |
| return False, '余额不足' |
| return func(balance=balance, pay_password=pay_password, *args, **kwargs) |
| |
| return inner |
| |
| return check_balance |
| |
| |
| |
| |
| |
| |
| @uuu(tag='login') |
| @uuu(tag='check', tag_func='') |
| def get_balance_jian(*args, **kwargs): |
| res = bank_data['heart']['balance'] - kwargs.get('balance') |
| bank_data['heart']['balance'] = res |
| print(bank_data) |
| return f"{kwargs.get('username')}提取金额:{kwargs.get('balance')},目前余额:{res}" |
| |
| |
| |
| |
| |
| |
| @uuu(tag='login') |
| @uuu(tag='check', tag_func='add') |
| def get_balance(*args, **kwargs): |
| res = bank_data['heart']['balance'] + kwargs.get('balance') |
| bank_data['heart']['balance'] = res |
| print(bank_data) |
| return f"{kwargs.get('username')}存款金额:{kwargs.get('balance')},目前余额:{res}" |
| |
| func_aa = ''' |
| 1.取钱 |
| 2.存钱 |
| 3.退出 |
| ''' |
| |
| while 1: |
| print(func_aa) |
| uname = input('请选择功能:>>>') |
| if uname == '1': |
| a = get_balance_jian() |
| if uname == '2': |
| a = get_balance() |
| print(a) |
| if uname == '3': |
| print('欢迎下次使用!') |
| break |
【五】伪装装饰器
| from functools import wraps |
| def wrapper(func): |
| def inner(*args, **kwargs): |
| ''' |
| # 这是验证登录的装饰器 |
| --- 确认账号是 heart 密码是 123 才是超级管理员 |
| ''' |
| return func(*args, **kwargs) |
| |
| return inner |
| |
| @wrapper |
| def add(): |
| ... |
| |
| print(help(add)) |
| print(f'----------------------------') |
| |
| def wrapper(func): |
| @wraps(func) |
| def inner(*args, **kwargs): |
| ''' |
| :param args: 可变长位置参数 |
| :param kwargs: 可变长关键字参数 |
| :return: |
| ''' |
| return func(*args, **kwargs) |
| |
| return inner |
| |
| @wrapper |
| def add(): |
| ... |
| |
| |
| print(help(add)) |
【六】语法糖
(1)多层语法糖嵌套
- 首先定义好装饰器功能,将需要添加功能的函数体代码放置在装饰器下方,将需要执行功能的装饰器语法糖按照执行的顺序放在原函数体函数名上方,多层语法糖加载顺序由下往上,函数内执行顺序是按语法糖顺序从上往下
| def outter1(func1): |
| print('加载了outter1') |
| def wrapper1(*args, **kwargs): |
| print('执行了wrapper1') |
| res1 = func1(*args, **kwargs) |
| return res1 |
| return wrapper1 |
| |
| def outter2(func2): |
| print('加载了outter2') |
| def wrapper2(*args, **kwargs): |
| print('执行了wrapper2') |
| res2 = func2(*args, **kwargs) |
| return res2 |
| return wrapper2 |
| |
| def outter3(func3): |
| print('加载了outter3') |
| def wrapper3(*args, **kwargs): |
| print('执行了wrapper3') |
| res3 = func3(*args, **kwargs) |
| return res3 |
| return wrapper3 |
| |
| |
| @outter1 |
| @outter2 |
| @outter3 |
| def index(): |
| print('from index') |
| print(index()) |
| |
| |
| |
| |
| |
| |
| |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通