装饰器拓展
一:装饰器的缺点,解决方法
正常的函数,可以使用内置方法打印函数名,注释等方便开发。
def f(*args, **kwargs): """ 这是一个测试装饰器的函数,没什么其他的用法 :param args: :param kwargs: :return: """ print("2018-06-04") print(f.__name__) print(f.__doc__) # f # 这是一个测试装饰器的函数,没什么其他的用法 # :param args: # :param kwargs: # :return:
在加上装饰器之后,使用内置方法打印函数名,变成了inner,注释成了None
def wrapper(func): def inner(*args, **kwargs): print("start") ret = func(*args, **kwargs) print("end") return ret return inner @wrapper def f(*args, **kwargs): """ 这是一个测试装饰器的函数,没什么其他的用法 :param args: :param kwargs: :return: """ print("hello world") print(f.__name__) print(f.__doc__) # inner # None
出现上面的情况的原因是因为函数经过装饰之后,实际上都是在执行inner函数。
为了避免这种情况,可以引入一个模块来解决这个问题
from functools import wraps def wrapper(func): @wraps(func) def inner(*args, **kwargs): print("start") ret = func(*args, **kwargs) print("end") return ret return inner @wrapper def f(*args, **kwargs): """ 这是一个测试装饰器的函数,没什么其他的用法 :param args: :param kwargs: :return: """ print("hello world") print(f.__name__) print(f.__doc__) # f # # 这是一个测试装饰器的函数,没什么其他的用法 # :param args: # :param kwargs: # :return:
二:多个装饰器的执行顺序
大多的认知仅是:多个装饰器的执行顺序从上到下,现在,通过一个例子来深入的分析一下装饰器装饰的函数的整个运行过程。
def wrapper_a(func): #执行结果:返回一个函数a
print(func.__name__+'_wrapper_a')
print('*' * 20)
def a(*args, **kwargs):
print('get inner a')
func(*args, **kwargs)
return a
def wrapper_b(func): #执行结果:返回一个函数b
print(func.__name__+'_wrapper_b')
print('*'*20)
def b(*args, **kwargs):
print('get inner b')
func(*args, **kwargs)
return b
@wrapper_a
@wrapper_b
def f():
print("hello world")
f()
# f_wrapper_b
# ********************
# b_wrapper_a
# ********************
# get inner a
# get inner b
# hello world
在被装饰函数f加括号调用时,函数f作为参数,被传入就近的装饰器wrapper_b中,然后装饰器wrapper_b开始执行。
#执行结果有三: 1:执行:print(func.__name__+'_wrapper_b')和print('*'*20) f_wrapper_b ******************** 2:执行:def b(*args, **kwargs):
.........
.........
定义了一个闭包函数a,此时:func = f, 3:执行:return b 把以func= f 包裹的闭包函数b作为返回值返回给调用者。 注意:此时函数b是定义阶段,不会执行函数体代码。
在得到返回值后,返回值作为一个参数,被传入装饰器wrapper_a中,然后装饰器wrapper_a开始执行。
注意:此时的返回值是一个闭包函数b,其代码如下:
func = f def b(*args, **kwargs): print('get inner b') func(*args, **kwargs)
wrapper_a执行的结果如下:
执行结果有三: 1:执行:print(func.__name__+'_wrapper_a')和print('*'*20) b_wrapper_a ******************** 2:执行: def a(*args, **kwargs): print('get inner a') func(*args, **kwargs) 定义了一个闭包函数a,此时:func = b, 3:执行:return a 把以func= b 包裹的闭包函数a作为返回值返回给调用者。 注意:此时函数a是定义阶段,不会执行函数体代码。
此时的返回值同样是一个闭包函数a,其代码如下:
func = b def a(*args, **kwargs): print('get inner a') func(*args, **kwargs)
综上所述:在被装饰函数f加括号调用后,代码运转得到一个返回值,并把这个返回值赋值给变量f,这样就得到了以下的代码:
#函数f func = b def a(*args, **kwargs): print('get inner a') func(*args, **kwargs)
而闭包函数b就是装饰器wrapper_b运行完成的结果
变量f是一个函数,加括号调用,就开始执行代码:
print('get inner a') #结果:get inner a
func(*args, **kwargs) # func = b,实际执行b(*args, **kwargs)
闭包函数b执行代码:
print('get inner b') #结果:get inner b
func(*args, **kwargs) # func = f,实际执行f(*args, **kwargs)
最后执行被装饰的函数f:
print("hello world") #结果:hello world
因此多个装饰器的执行效果在展现上是从上到下的顺序:
# get inner a # get inner b # hello world
但是现在已经可以知道,这只是一种假象,实际的执行来说,是从最近的装饰器开始执行:
# f_wrapper_b # ******************** # b_wrapper_a # ********************
上面的执行结果很好的解释了这一点。
因此可以说:
装饰器从执行效果来说是:从上到下;
从实际的执行来说:是从最近的装饰器开始。
Ideal are like the stars --- we never reach them ,but like mariners , we chart our course by them