zoukankan      html  css  js  c++  java
  • 052 装饰器

    一、什么是装饰器

    装饰器: 装饰的工具(函数),这个函数有装饰的作用.可以理解为装饰器就是一个函数的加工厂

    装饰器本质: 装饰器 就是一个函数A装饰的对象 也就是一个函数B
    函数B中都是一些功能性的代码,比如函数B中是买东西的逻辑代码而函数A则是一些通用的功能修饰的代码,比如函数A中是登陆验证等等。
    现在给一个需求,需要在购物之前加上身份验证,我们就可以使用函数A去装饰函数B,使得函数B具有(函数A+函数B)的全部功能。给函数加功能,但不改变函数的代码和调用方式

    装饰器的实现必须遵循两大原则:

    1. 不改变函数B的调用方式
    2. 不改变函数B的源代码
    def A():
        """装饰器"""
        pass
    
    def B():
        """被装饰的函数"""
        pass
    
    B()
    

    需要注意的是

    • 装饰器本身其实是可以任意可调用的对象
    • 被装饰的对象也可以是任意可调用的对象

    二、无参装饰器(双层装饰器)

    2.1 双层装饰器:无参、无返回值的被装饰函数

    例:检测index函数的运行的时间,但是不能改变index函数的调用方式,以及index函数的源码

    # 写一个装饰器,装饰写好的功能函数,相当于加工厂
    def outer(func):   # func = 真正的index
        def inner():
            start = time.time()
            func()
            end = time.time()
            print(end-start)
        return inner	# 返回inner函数对象
    
    # 功能函数
    import time
    def index():
        print('hello index')
        time.sleep(1)
    
    # 使用装饰器
    index = outer(index)  # 函数对象做形参,得到闭包函数对象
    index()		# 调用闭包函数
    

    hello index
    1.0521571636199951

    2.2 双层装饰器:带参,有返回值的被装饰函数

    例:检测index函数的运行的时间,但是不能改变index函数的调用方式,以及index函数的源码

    # 双层装饰器:带参,有返回值的被装饰函数
    import time
    
    def outer(func):
        def inner(*args, **kwargs):		# 可变参数
            print('args:',args)  # (10,)
            print('kwargs:',kwargs)
            start = time.time()
            res = func(*args, **kwargs)  # 真正的index() 解压缩
            end = time.time()
            print("执行时间:",end - start)
            return res
        return inner	# 返回加工后的对象
    
    def index(a=1):
        print('a', a)
        print('hello index')
        time.sleep(1)
        return "执行完了"
    
    index = outer(index)	# 函数对象做参数,得到一个闭包函数对象
    print(index())
    

    args: ()
    kwargs: {}
    a 1
    hello index
    执行时间: 1.0005826950073242
    执行完了

    三、装饰器语法糖

    装饰器语法糖:就是在被装饰函数正上方,并且是单独一行写上@装饰器名

    对之前的代码稍作修改,加入语法糖

    # 双层装饰器:带参,有返回值的被装饰函数
    import time
    
    def outer(func):
        def inner(*args, **kwargs):		# 可变参数
            print('args:',args)  # (10,)
            print('kwargs:',kwargs)
            start = time.time()
            res = func(*args, **kwargs)  # 真正的index() 解压缩
            end = time.time()
            print("执行时间:",end - start)
            return res
        return inner	# 返回加工后的对象
    
    @outer # 语法糖(更精简的代码)   相当于 index = deco(index)
    def index(a=1):
        print('a', a)
        print('hello index')
        time.sleep(1)
        return "执行完了"
    
    # 使用了语法糖的被装饰函数可以直接调用
    print(index(3))
    

    args: (3,)
    kwargs: {}
    a 3
    hello index
    执行时间: 1.0002954006195068
    执行完了

    四、有参装饰器(三层装饰器)

    三层装饰器: 就是给双层装饰器加参数

    例:在购物函数之前加上登陆功能,并判断账号密码的来源

    # 三层装饰器: 给双层装饰器加参数
    
    # 判断账号密码来自于哪个地方
    def auth(engine):   # 来源
        def login(func):    # 函数对象形参
            def inner(*args, **kwargs):     # 形参
                # 登录功能
                if engine == 'file':
                    username = input('usrename:')
                    pwd = input('pwd:')
                    if username == 'nick' and pwd == '123':
                        print('登录成功')
                        res = func(*args, **kwargs)  # shopping()   # 实参
                        return res
                    else:
                        print('登录失败')
    
                elif engine == 'db':
                    print('账号密码来自于数据库,非法请求')
    
            return inner
        return login
    
    @auth('db')     # 使用语法糖给装饰器传参
    def shopping():
        print('shopping')
    
    # 使用了语法糖的被装饰函数可以直接调用,但先判断了装饰器的参数
    print(shopping(3))
    

    账号密码来自于数据库,非法请求
    None

    五、装饰器模板

    所谓前人栽树,后人乘凉。你写框架,我用模板。会用就行了。

    5.1 双层装饰器模板

    # 双层装饰器模板
    def outter(func): # 函数对象形参
        def inner(*args, **kwargs):   # 函数形参
            # 加功能
            print(f"这里是函数参数args:{args}, kwargs:{kwargs}")
            res = func(*args, **kwargs)   # 解压缩 func是被装饰的函数 函数实参
            return res
    
        return inner
    
    @outter     # 语法糖(表示使用outter函数进行装饰) 相当于 func = outter(func)
    def func(*args, **kwargs):
        print('双层装饰器模板')
        
    # 使用
    func("位置参数1",kv="关键字参数1")  # 可以传递任意实参
    

    这里是函数参数args:('位置参数1',), kwargs:{'kv': '关键字参数1'}
    双层装饰器模板

    5.2 三层装饰器模板

    # 三层装饰器模板: 给双层装饰器加参数的
    def outter(engine): # 给装饰器传参
        def inner(func):    # func是真正的功能函数
            def wrapper(*args, **kwargs):  # wrapper是未来要运行的函数
                # 加功能
                print(f"这里是装饰器参数engine:{engine}")
                print(f"这里是函数参数args:{args}, kwargs:{kwargs}")
                res = func(*args, **kwargs)  # func是被装饰的函数
                return res
    
            return wrapper
    
        return inner
    
    
    # 语法糖(表示使用outter函数进行装饰并传参)
    # 相当于inner = outter("file")
    # func = inner(func)
    @outter('装饰器参数')
    def func(*args, **kwargs):
        print('三层装饰器模板')
    
    # 使用
    func("位置参数1",kv="关键字参数1")  # 可以传递任意实参
    

    这里是装饰器参数engine:装饰器参数
    这里是函数参数args:('位置参数1',), kwargs:{'kv': '关键字参数1'}
    三层装饰器模板

    总结:

    其实我是不太喜欢写总结的。。。但是因为这章比较难,怕读者理解不深刻,还是总结一下。嘿嘿

    其实我个人理解。装饰器就相当于是一个加工厂。可以这么认为。

    把你需要加工的函数放到装饰器中会得到一个加工后的函数对象。直接调用他就有了装饰器之后的功能。

    其实原理就是之前说的闭包函数。通过闭包函数返回的函数对象进行调用。就有了闭包函数中的一些元素和功能。

    重点来了。实际上大家只要会用装饰器+语法糖的格式就可以了。实在不行,不是还有装饰器模板嘛。嘿嘿嘿

  • 相关阅读:
    T-SQL 数据库数据的高级查询
    数据库 T-sql 基础语句
    数据库的定义、关系型数据库的四种约束。。
    linux上使用crontab任务调度
    pip list 显示出以下错误: DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] se
    python报错:NameError: name 'converter' is not defined
    python3报错:TypeError: can't concat bytes to str
    Fiddler如何手机抓包
    数据分析----VBA的使用
    Excel进行数据分析
  • 原文地址:https://www.cnblogs.com/XuChengNotes/p/11341354.html
Copyright © 2011-2022 走看看