装饰器、装饰器推导、装饰器语法糖

【一】装饰器介绍

【1】什么是装饰器

# 装饰代指的就是为被装饰的对象添加新的功能
# 器 代表工具 增加后的调用方式不变
# 在不改变原函数代码和调用方式的基础上增加额外的新功能

【2】装饰器的用途

# 对上线后的程序进行新功能的增加和修改
# 给一个功能增加新的需求或者改变原来的程序的运行逻辑

【3】装饰器的分类

# 【1】有参装饰器
# 【2】无参装饰器

【4】装饰器的原理

# 闭包函数 + 函数对象的组合使用

【二】无参装饰器

【1】什么是无参装饰器

# 无参 : 没有参数
# 没有参数的装饰器

【2】推导装饰器的语法

  • 要为一段代码增加计时功能

(1)面条版

import time

# 记录程序运行起始时间
start_time = time.time()

# 程序运行主代码逻辑
print(f"我开始睡觉了 .... ")
time.sleep(2)
print(f"我结束睡觉了 .... ")

# 记录程序运行结束时间
end_time = time.time()

# 总结总耗时
print(f"当前程序总耗时 :>>>>  {end_time - start_time} s")

(2)函数版

  • 用函数封装需要计时的代码,调用函数即可
import time

def show():
    # 程序运行主代码逻辑
    print(f"我开始睡觉了 .... ")
    time.sleep(2)
    print(f"我结束睡觉了 .... ")
    
    
def show_show():
    # 程序运行主代码逻辑
    print(f"我开始睡觉了 .... ")
    time.sleep(1)
    print(f"我结束睡觉了 .... ")


# 记录程序运行起始时间
start_time = time.time()

show()
# show_show()

# 记录程序运行结束时间
end_time = time.time()

# 总结总耗时
print(f"当前程序总耗时 :>>>>  {end_time - start_time} s")

(3)闭包函数

import time

def show():
    # 程序运行主代码逻辑
    print(f"我开始睡觉了 .... ")
    time.sleep(2)
    print(f"我结束睡觉了 .... ")
    
    
def show_show():
    # 程序运行主代码逻辑
    print(f"我开始睡觉了 .... ")
    time.sleep(1)
    print(f"我结束睡觉了 .... ")
    

def outer(func):
    # func = show_show
    def inner():
        func()
        print(func)

    return inner

# 本质
# inner = outer(show)
# 但是函数的返回值可以重新命名
# show_show = outer(show_show)
# show_show()

【3】装饰器推导

(1)需求

# 定义一个用户数据字典
# 校验当前用户名和密码是否正确,如果正确则打印登录成功并取款
# 要求 调用还是 withdral() 
# 但是会先验证用户名和密码

(2)普通版-修改源代码

user_data_dict = {'username': "max", "password": "123"}

# 直接在原函数的基础上额外新增代码
def withdral():
    username = input("username :>>>> ")
    password = input("password :>>>> ")
    # 否则打印失败
    if username == user_data_dict.get('username') and password == user_data_dict.get('password'):
        print(f"登录成功")
        print(f"这是登陆成功后才能看到的动作片")
    else:
        print("登录失败")
        
def transform():
    username = input("username :>>>> ")
    password = input("password :>>>> ")
    # 否则打印失败
    if username == user_data_dict.get('username') and password == user_data_dict.get('password'):
        print(f"登录成功")
        print(f"转账功能")
    else:
        print("登录失败")
        
withdral()
        
transform()

(2)闭包函数-增加校验逻辑

def outer(func):
    def inner():
        username = input("username :>>>> ")
        password = input("password :>>>> ")
        # 否则打印失败
        if username == user_data_dict.get('username') and password == user_data_dict.get('password'):
            print(f"登录成功")
            func()
        else:
            print("登录失败")

    return inner


def transform():
    print(f"这是在转账功能")
transform = outer(transform)
transform()

# transform = outer(transform)  # transform  = inner = outer(transform)
# transform()  # transform() = inner() = outer(transform)()

def withdral():
    print(f"这是取款功能")
withdral = outer(withdral)
# withdral()

【4】无参装饰器模板

# 无参装饰器的模板
def outer(func):
    ''' 
    :param func: 每一次需要调用传递的函数
    :return:返回值是内嵌函数 inner 的函数内存地址 
    '''
    def inner():
        # 可以加逻辑校验
        # 当符合指定条件后,可以允许执行传递进来的函数
        # 不符合条件的时候不执行函数
        func()
        # 做逻辑校验,func 函数执行后的逻辑

    return inner

【三】有参装饰器

【1】无参装饰器

# 无参装饰器无法传递参数
def outer(func):
    def inner():
        func()

    return inner


def transform(username, money):
    print(f"向 {username} 转账 {money}")

transform = outer(transform)
transform() # 报错

【2】有参装饰器

# 根据指定的位置参数传递参数
def outer(func):
    def inner(username, money):
        func(username, money)

    return inner


def transform(username, money):
    print(f"向 {username} 转账 {money}")
username = 'qwe'
money= 10000
transform = outer(transform)
transform(username,money)

【3】有参装饰器优化

# 用可变长位置参数和可变长关键字参数接收到函数所需要的所有参数
def outer(func):
    def inner(*args,**kwargs):
        func(*args,**kwargs)

    return inner


def transform(username, money):
    print(f"向 {username} 转账 {money}")
username = 'qwe'
money= 10000
transform = outer(transform)
transform(username,money)

【4】有参装饰器模板

def outer_no(func):
    def inner():
        func()

    return inner


def outer_yes(func):
    def inner(*args, **kwargs):
        func(*args, **kwargs)
    return inner

【四】装饰器语法糖

【1】无参装饰器语法糖

(1)没有语法糖的时候

import time

def timer(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(f"总耗时 :>>>> {end - start} s")

    return inner

def transform():
    time.sleep(1)

transform = timer(transform)
transform()

(2)有语法糖的时候

def timer(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(f"总耗时 :>>>> {end - start} s")

    return inner

@timer
def transform():
    time.sleep(1)

# transform = timer(transform)
# transform()

transform()

【2】有参装饰器语法糖

def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)
        end = time.time()
        print(f"总耗时 :>>>> {end - start} s")

    return inner
@timer
def transform(username,money):
    print(f"{username} | {money}")
    time.sleep(1)

# transform = timer(transform)
# transform()

transform("max",500)

【3】有参语法糖

(1)单语法糖

login_dict = {'username': 'max'}


def login_auth(func):
    def inner(*args, **kwargs):
        if login_dict.get('username'):
            func(*args, **kwargs)
        else:
            print(f"请先登录!")

    return inner


def timer(func):
    def inner(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print(f"总耗时 :>>>> {end - start} s")

    return inner


@login_auth
def transform(username, money):
    print(f"{username} | {money}")
    time.sleep(1)


# transform = timer(transform)
# transform()
# 现在只能使用一种装饰器并且在装饰器中不能切换
transform("max", 500)

(2)多语法糖

login_dict = {'username': None}


def decrator(tag):
    '''

    :param tag: 'login_auth' ---> 登录认证装饰器
    'timer' ---> 返回 计时装饰器
    :return:
    '''
    if tag == "login_auth":
        def login_auth(func):
            def inner(*args, **kwargs):
                if login_dict.get('username'):
                    func(*args, **kwargs)
                else:
                    print(f"请先登录!")
            return inner
        return login_auth
    elif tag == "timer":
        def timer(func):
            def inner(*args, **kwargs):
                start = time.time()
                func(*args, **kwargs)
                end = time.time()
                print(f"总耗时 :>>>> {end - start} s")

            return inner

        return timer


def transform(username, money):
    print(f"{username} | {money}")
    time.sleep(1)

# transform = decrator(tag="login_auth")(transform)
# transform('chosen',999)
# print(transform)

transform = decrator(tag="timer")(transform)
transform('chosen',999)
print(transform)

(3)多语法糖优化

login_dict = {'username': None}


def decrator(tag):
    '''

    :param tag: 'login_auth' ---> 登录认证装饰器
    'timer' ---> 返回 计时装饰器
    :return:
    '''
    if tag == "login_auth":
        def login_auth(func):
            def inner(*args, **kwargs):
                if login_dict.get('username'):
                    func(*args, **kwargs)
                else:
                    print(f"请先登录!")
            return inner
        return login_auth
    elif tag == "timer":
        def timer(func):
            def inner(*args, **kwargs):
                start = time.time()
                func(*args, **kwargs)
                end = time.time()
                print(f"总耗时 :>>>> {end - start} s")

            return inner

        return timer
    else:
        print(f"无参数的",tag)

# @decrator(tag='login_auth')
@decrator(tag='timer')
def transform(username, money):
    print(f"{username} | {money}")
    time.sleep(1)

transform("chosen",999)

【4】多层语法糖案例

(1)不使用语法糖

def dec_a(func):
    # 【二】调用 dec_a 函数触发
    print('111')

    def wrapper(*args, **kw):
        # 【三】执行 main_func()
        print('222')
        func(*args, **kw)  # b_wrapper(main_func)
        # 【七】结束上面的 b_wrapper 回来
        print('333')

    return wrapper


def dec_b(func):
    # 【一】进来 dec_b 函数
    print('aaa')

    def wrapper(*args, **kw):
        # 【四】因为在 dec_a 的 wrapper 里面调用了 func --> 就是我自己
        print('bbb')
        func(*args, **kw)  # main_func
        # 【六】执行完 main_func 又回来继续走
        print('ccc')

    return wrapper


@dec_a
@dec_b # 多层语法糖的时候谁在下先调用谁,但是执行校验的时候是谁在上先校验谁
def main_func():
    # 【五】回到真正的 main_func
    print('main_func')

'''
# 定义顺序是一个个挨着定义
# 调用顺序是倒着谁在下先调用谁
main_func = dec_b(main_func)
# main_func = b_wrapper (main_func)
main_func = dec_a(main_func)
# main_func = a_wrapper (b_wrapper(main_func))
main_func()
# a_wrapper

# aaa
# 111
# 222
# bbb
# main_func
# ccc
# 333
'''

main_func()


# 指定结果打印顺序

(2)使用语法糖

def dec_a(func):
    # 【二】调用 dec_a 函数触发
    print('111')

    def wrapper(*args, **kw):
        # 【三】执行 main_func()
        print('222')
        func(*args, **kw)  # b_wrapper(main_func)
        # 【七】结束上面的 b_wrapper 回来
        print('333')

    return wrapper


def dec_b(func):
    # 【一】进来 dec_b 函数
    print('aaa')

    def wrapper(*args, **kw):
        # 【四】因为在 dec_a 的 wrapper 里面调用了 func --> 就是我自己
        print('bbb')
        func(*args, **kw)  # main_func
        # 【六】执行完 main_func 又回来继续走
        print('ccc')

    return wrapper

# 多层语法糖的时候谁在下先调用谁,但是执行校验的时候是谁在上先校验谁
@dec_a
@dec_b
def main_func():
    # 【五】回到真正的 main_func
    print('main_func')

'''
# aaa
# 111
# 222
# bbb
# main_func
# ccc
# 333
'''

main_func()
posted @ 2024-04-17 18:53  光头大炮  阅读(10)  评论(0编辑  收藏  举报