装饰器高级

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的爱情故事 的故事扩展部分功能,掌握装饰器用法

原文地址:https://www.cnblogs.com/sunBinary/p/10940867.html