zoukankan      html  css  js  c++  java
  • 装饰器高级

    Date: 2019-05-28

    Author: Sun

    装饰器模板

    ​ 装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。

    from functools import wraps
    
    def fn_wrapper(func):   #外部函数
        print("##################")
        @wraps(func)  #此处加上@wraps包裹被装饰函数,目的是为了让被装饰的函数文档对外可见
        def _wrapper(*args, **kwargs):   #内部函数
            '''
            装饰器内部函数说明文档
            :param args:
            :param kwargs:
            :return:
            '''
            print(f"before {func.__name__} called, can add some functions.")
            res = func(*args, **kwargs)   #在此处真正的被调用, 调用在这里!!!!!
            print(f"after {func.__name__} called., can also add some other functions.")
            return res
        return _wrapper
    

    以上就是一个标准的装饰器模板。(不带参数)

    带参数装饰器

    为何要引入带参数装饰器?

    ​ 因为有时候我们想通过装饰器传入一些参数在外部来控制一些逻辑

    inspect模块

    ​ 此处要用到python中非常重要的一个库inspect模块,用于收集python对象的信息,可以获取类或函数的参数的信息,源码,解析堆栈,对对象进行类型检查等等。

    提取函数签名python3 inspect.signature()
    带参数的装饰器

    # -*- coding: utf-8 -*-
    __author__ = 'sun'
    __date__ = '2019/5/28 19:09'
    
    from inspect import signature
    
    import inspect
    
    def func(a, b=0, *c, d, e=1, **f):
        pass
    
    aa = inspect.signature(func)
    print("inspect.signature(fn)是:%s" % aa)
    print("inspect.signature(fn)的类型:%s" % (type(aa)))
    print("\n")
    

    输出的结果是:

    inspect.signature(fn)是:(a, b=0, *c, d, e=1, **f)
    inspect.signature(fn)的类型:<class 'inspect.Signature'>
    

    此处对函数签名,能把函数的定义类型都给获取到

    案例分析

    如下是一个采用带参数装饰器实现一个对函数参数进行类型检查

    # -*- coding: utf-8 -*-
    __author__ = 'sun'
    __date__ = '2019/5/28 19:09'
    
    from inspect import signature
    
    def check_type(*ty_args, **ty_kwargs):
      def out_wrapper(func):
        # 通过signature方法,获取函数形参:name, age, height
        sig = signature(func)
        # 获得装饰器传来的参数, 函数签名与之绑定,字典类型
        bind_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments
        print(bind_types)
    
        def wrapper(*args, **kwargs):
          # 给执行函数中具体的实参进行和形参进行绑定,形成字典的形式
          func_type = sig.bind(*args, **kwargs).arguments.items()
          print(func_type)
          # 循环形参和实参字典的items()形式
          for name, obj in func_type:
            if name in bind_types:
              if not isinstance(obj, bind_types[name]):
                raise TypeError('%s must be %s' % (name, bind_types[name]))
          func(*args, **kwargs)
        return wrapper
      return out_wrapper
    
    
    # 通过装饰器实现对函数参数进行类型检查
    @check_type(str, int, float)
    def func(name, age, height):
      print(name, age, height)
    
    
    if __name__ == '__main__':
      func('china', 100, 2.75)
    
    # -*- coding: utf-8 -*-
    __author__ = 'sun'
    __date__ = '2019/5/28 20:58'
    
    from functools import wraps
    import time
    
    '''
    装饰器模板
    
    '''
    #所有不带参数装饰器标准模板
    def deco_templ(func): #装饰器外层装饰函数
        @wraps(func)
        def _wrapper(*args, **kwargs):   #装饰器内层函数,装饰被装饰函数的参数,必须是可变参数
            #里层函数返回不是func函数首地址,返回func函数调用结果
            #TODO add some functions before func called.
    
            result = func(*args, **kwargs)
            #TODO add some functions after func called.
    
            return result
        return _wrapper   #python是闭包?python装饰器就是一个闭包应用
    
    
    
    
    #统计函数调用时间装饰器
    def time_it(func): #装饰器外层装饰函数
        @wraps(func)
        def _wrapper(*args, **kwargs):   #装饰器内层函数,装饰被装饰函数的参数,必须是可变参数
            #里层函数返回不是func函数首地址,返回func函数调用结果
            #TODO add some functions before func called.
            begin_time = time.time()
            result = func(*args, **kwargs)
            #TODO add some functions after func called.
            end_time = time.time()
            use_time = end_time - begin_time
            print(f"function:{func.__name__} use total time :{use_time} s")
            return result
        return _wrapper   #python是闭包?python装饰器就是一个闭包应用
    
    
    @time_it
    def eat(name, content):
        time.sleep(1.5)
        print(f"people {name} eat {content}")
    
    
    
    
    eat('xiaoming', 'kfc')
    
    
    执行结果:
    xiaoming eat kfc
    func:eat use time 1.5155751705169678
    
    # -*- coding: utf-8 -*-
    __author__ = 'sun'
    __date__ = '2019/5/28 21:25'
    from functools import wraps
    '''
    带参数的装饰器
    在原有不带参数装饰器基础上,外面再加一层, 返回结果是第二层函数
    总共3层
    '''
    
    #需求:
    #  函数调用前加上权限校验,不同的装饰器参数,校验机制不一样
    
    #所有带参数装饰器标准模板
    def deco_params(*pr_args, **pr_kwargs):    #第一层
        def deco_func(func): #装饰器外层装饰函数    #第二层
            @wraps(func)
            def _wrapper(*args, **kwargs):   #装饰器内层函数,装饰被装饰函数的参数,必须是可变参数, 第三层
                #里层函数返回不是func函数首地址,返回func函数调用结果
                #TODO add some functions before func called.
    
                result = func(*args, **kwargs)
                #TODO add some functions after func called.
    
                return result
            return _wrapper   #python是闭包?python装饰器就是一个闭包应用
        return deco_func
    
    def fprint1():
        print("11111校验")
        return  '111 ok'
    
    def fprint2():
        print("22222校验")
        return '2222 ok'
    
    LEVEL_CHECK_MAP = {
        1:fprint1,
        2:fprint2,
    }
    
    #装饰器
    def check_level(*cl_args, **cl_kwargs):
        def deco_func(func):  # 装饰器外层装饰函数    #第二层
            @wraps(func)
            def _wrapper(*args, **kwargs):  # 装饰器内层函数,装饰被装饰函数的参数,必须是可变参数, 第三层
                # 里层函数返回不是func函数首地址,返回func函数调用结果
                # step1, 校验过程, check
                level = cl_args[0]
                weight = cl_kwargs.get('weight', 1)  #如果没有weight,默认赋值1
                check_func = LEVEL_CHECK_MAP.get(level,None)
                if check_func == None:   #不是1或者2
                    print("not match key.")
                    return
                fprint = LEVEL_CHECK_MAP[level]
                check_res = fprint()
                print(f"check_res:{check_res}, weight={weight}")
                # if level == 1:   #在函数调用前做校验
                #     print("-------权限校验1逻辑处理-----")
                # elif level == 2:
                #     print("------权限校验2逻辑处理------")
                #step2, func called
                result = func(*args, **kwargs)
                # TODO add some functions after func called.
                return result
            return _wrapper  # python是闭包?python装饰器就是一个闭包应用
    
        return deco_func
    
    
    @check_level(1, weight=10)  #1级别权限校验
    def test1():
        print("this is test1 function")
    
    
    @check_level(2, weight=20)    #2级别的权限校验
    def test2(name, content):
        print("this is test2 function")
    
    @check_level(2)
    def test3():
        print("this is test3 function.")
    
    #
    # def test_args(*args, **kwargs):
    #     print(args)
    #     value_12 = args[1]
    #     print(value_12)
    #     print(kwargs)
    #
    #
    # test_args(12, 3,4,5, name='zhou')
    
    
    
    test1()
    
    test2('zhou','fdk')
    
    test3()
    
    执行结果:
    11111校验
    check_res:111 ok, weight=10
    this is test1 function
    22222校验
    check_res:2222 ok, weight=20
    this is test2 function
    22222校验
    check_res:2222 ok, weight=1
    this is test3 function.
    

    案例分析:

    (1)实现统计函数时间装饰器

    (2)带参数装饰器实现权限控制

    (3)采用带参数装饰器实现超时重试机制函数

    (4)通过分析 小明和baby的爱情故事 的故事扩展部分功能,掌握装饰器用法

  • 相关阅读:
    修改SharePoint 2013中item Created by 信息
    用powershell批量新增user profile
    如何发一封回复的时候收件人和发件人不同的邮件
    SharePoint 2013配置 Workflow Manager
    正则表达式
    go
    HTML5
    js-example
    css3
    jquery-example
  • 原文地址:https://www.cnblogs.com/sunBinary/p/10940867.html
Copyright © 2011-2022 走看看