装饰器

装饰器

  • 装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能
  • 装饰器经常用于有切面需求的场景
    • 插入日志、性能测试、事务处理、缓存、权限校验等应用场景
    • 有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用

【一】装饰器的作用

  • 软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。
    • 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
    • 对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
  • 软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃
    • 而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

【二】装饰器的分类

  • 函数装饰器分为:无参装饰器和有参装饰器
  • 二者的实现原理一样,都是函数嵌套+闭包+函数对象的组合使用的产物

【三】无参装饰器

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):
        # 这里写调用 func函数之前的逻辑
        res = func(*args,**kwargs)
        # 这里写调用 func函数之后的逻辑
        return res

    return inner
@outer
def add(*args,**kwargs):
    return 1
print(add())

(2)看电影练习

# 看电影
# 用户信息字典 --- 定义用户名 和 年龄
# 大于 18 岁 看电影
# 小于 18 岁  18禁

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())

## 加载了outter3
# 加载了outter2
# 加载了outter1
# 执行了wrapper1
# 执行了wrapper2
# 执行了wrapper3
# from index
posted @ 2023-12-13 10:14  ALPACINO6  阅读(26)  评论(0)    收藏  举报