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