一、多个装饰器的执行顺序
前面我们说到了装饰其的用法,以及他的作用;可是在工作中我们经常能看到装饰器可能不止使用一次,在同一个函数下我们有可能加上好几个装饰器,那他们的运行机制:
例:
def decorator_a(func): print('count 1') def inner_a(*args, **kwargs): print('count 2') return func(*args, **kwargs) return inner_a def decorator_b(func): print('count 3') def inner_b(*args, **kwargs): print('count 4') return func(*args, **kwargs) return inner_b # 当有多个装饰器时, 从下到上调用装饰器; @decorator_b # f = decorator_b(inner_a) # f = inner_b @decorator_a # f = decorator_a(f) # f = inner_a def f(x): print('Get in f') return x * 2 f(1) # inner_b(1)
我们看到了上面的结果,函数在调用装饰器时是自下向上的顺序进行的;
多个装饰器我认为:函数调用是从下到上,但是装饰器最终要找到最后一层的函数;以上面的例子来说,当函数发现有装饰器时,系统中已经默认把装饰器放在内存中了,它先去找decorator_a,发现她并不是最底层函数,向下一个装饰器b读取,当把装饰器b陨星完时,发现这才是最底层的函数,那么它又安原来的路线逐级返回;说的并不是特别清楚,我用几个例字实践:
例如:我们可以多定义几个装饰器,从而寻找他的运行规律;
def decorator_a(func): print('函数a_1') def inner_a(*args, **kwargs): print('函数a_2') return func(*args, **kwargs) return inner_a def decorator_b(func): print('函数b_1') def inner_b(*args, **kwargs): print('函数b_2') return func(*args, **kwargs) return inner_b def decorator_c(func): print('函数c_1') def inner_a(*args, **kwargs): print('函数c_2') return func(*args, **kwargs) return inner_a def decorator_d(func): print('函数d_1') def inner_a(*args, **kwargs): print('函数d_2') return func(*args, **kwargs) return inner_a # 当有多个装饰器时, 从下到上调用装饰器; @decorator_d @decorator_c @decorator_b @decorator_a def f(x): print('Get in f') return x * 2 f(1)
这样的理解方式是不太好,没有从源码的底层上去理解;但是就目前而言我能够想的通,可能并不准确。如果正在看的你是个大佬那还请你多提一些建议,好让我有更深层次的理解
二、多个装饰器的应用
例:在实际的应用场景中,会采用多个装饰器先验证是否登陆成功,再验证权限是否足够;
import functools import inspect def is_admin(fun): # fun=add_student @functools.wraps(fun) def wrapper(*args, **kwargs): # kwargs = {'name':'root'} # inspect.getcallargs返回一个字典, key值为:形参, value值为对应的实参; inspect_res = inspect.getcallargs(fun, *args, **kwargs) print("inspect的返回值: %s" %(inspect_res)) if inspect_res.get('name') == 'root': temp = fun(*args, **kwargs) return temp else: print("Error:not root/admin user, no permisson add student") # 抛出异常 return wrapper login_seesion = ['root', 'admin', 'redhat'] def is_login(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): if args[0] in login_seesion: temp = fun(*args, **kwargs) return temp else: print("Error: %s未登陆!" %(args[0])) return wrapper @is_login @is_admin # is_admin() def add_student(name): print("添加学生信息.....") add_student('admin')
1、is_admin函数会帮你判断你所登陆的用户是否有权限
2、is_login函数判断你是否登陆了
我们先不运行程序,先来数理一遍;首先我们调用的is_admin函数,在运行完is_admin外层函数时发现还有一个装饰器函数,然后把参数传向is_login函数,把is_login函数外层函数运行结束后,系统发现并没有第三个装饰器,那么is_login里的warpper函数就是最底层函数,那么它就依次执行完is_login里的所有函数,再原路返回去寻找上一层函数直到函数运行结束;
三、带有参数装饰器练习_参数检测
上面我们说的都是无参装饰器,如果给装饰器戴上参数呢?
2. 升级版(有参数的装饰器)
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型
def required_types(*types): def required(f): def wrapper(*args, **kwargs): # args=(1,2) # 1. 判断每一个元素都是整形; for i in args: if not isinstance(i, types): print("Error:参数必须为",types) break else: res = f(*args, **kwargs) return res return wrapper return required @required_types(int,float) #这里的参数可以根据题目要求进行更改 def add(x, y): return x + y print(add(1,2))