获取被包装函数原生属性
例 1:常规函数取函数名
def func():
print('执行中')
print(func.__name__)
func()
# result:
# 执行中
# func
常规函数可以通过函数的 __name__
属性可拿到当前函数名称。
例 2:被装饰函数取函数名
def wrapper(func):
def inner():
print('执行前')
result = func()
print('执行后')
return result
return inner;
@wrapper
def func():
print('执行中')
print(func.__name__)
func()
# result:
# 执行前
# 执行中
# inner
# 执行后
问题:通过执行结果会发现,结果中想获取的函数名是 func
,而实际结果是 inner
。原因是 @wrapper
进行包装相当于执行一个操作:func=wrapper(func)=inner
。
要解决这个问题可以使用 functools
模块:
from functools import wraps
def wrapper(func):
# <1>
@wraps(func)
def inner():
print('执行前')
result = func()
print('执行后')
return result
return inner;
@wrapper
def func():
print('执行中')
print(func.__name__)
func()
# result:
# 执行前
# 执行中
# func
# 执行后
导入 functools
模块后通过 <1>
处操作,会发现执行的函数即使被包装但还是能获取到它本身的属性。
带参数的装饰器
示例
def wrapper(func):
def inner():
print('执行前')
result = func()
print('执行后')
return result
return inner;
@wrapper
def func_1():
print('执行中')
@wrapper
def func_2():
print('执行中')
@wrapper
def func_3():
print('执行中')
...
@wrapper
def func_n():
print('执行中')
问题:通过上述代码会发现,有很多函数都用了同一个装饰器,如果有一天要取消这些函数上的装饰,就必须对每一个函数进行修改。
解决
定义一个全局变量 flag
,并给原本装饰器外部再加一层函数用来接收参数,inner
函数内部通过外部参数 flag
判断被装饰函数的执行与否,修改 flag
即可控制装饰器的执行结果,如下:
from functools import wraps
flag = True
def wrapper_out(flag):
def wrapper(func):
@wraps(func)
def inner():
if (flag):
print('{}执行前'.format(func.__name__))
result = func()
print('{}执行后'.format(func.__name__))
else:
result = func()
return result
return inner
return wrapper
@wrapper_out(flag)
def func_1():
print('{}执行中'.format(func_1.__name__))
@wrapper_out(flag)
def func_2():
print('{}执行中'.format(func_2.__name__))
@wrapper_out(flag)
def func_3():
print('{}执行中'.format(func_3.__name__))
...
@wrapper_out(flag)
def func_n():
print('{}执行中'.format(func_n.__name__))
func_1()
func_2()
func_3()
func_n()
#result:
# func_1执行前
# func_1执行中
# func_1执行后
# func_2执行前
# func_2执行中
# func_2执行后
# func_3执行前
# func_3执行中
# func_3执行后
# func_n执行前
# func_n执行中
# func_n执行后
from functools import wraps
flag = False
def wrapper_out(flag):
def wrapper(func):
@wraps(func)
def inner():
if (flag):
print('{}执行前'.format(func.__name__))
result = func()
print('{}执行后'.format(func.__name__))
else:
result = func()
return result
return inner
return wrapper
@wrapper_out(flag)
def func_1():
print('{}执行中'.format(func_1.__name__))
@wrapper_out(flag)
def func_2():
print('{}执行中'.format(func_2.__name__))
@wrapper_out(flag)
def func_3():
print('{}执行中'.format(func_3.__name__))
...
@wrapper_out(flag)
def func_n():
print('{}执行中'.format(func_n.__name__))
func_1()
func_2()
func_3()
func_n()
#result:
# func_1执行中
# func_2执行中
# func_3执行中
# func_n执行中
多个装饰器装饰同一个函数
代码
def wrapper1(func):
def inner():
print('wrapper1 ,before func')
func()
print('wrapper1 ,after func')
return inner
def wrapper2(func):
def inner():
print('wrapper2 ,before func')
func()
print('wrapper2 ,after func')
return inner
@wrapper1
@wrapper2
def f():
print('in f')
f()
# result:
# wrapper1 ,before func
# wrapper2 ,before func
# in f
# wrapper2 ,after func
# wrapper1 ,after func
图解
从上图可以看到,从 1-9
步是装饰器的装载过程,10-18
步是执行过程。
结论:多个装饰器装饰同一个函数时,装载顺序是从下到上,但执行顺序却是从上到下,可以理解为创建了一个装饰器栈(先进后出)。
评论区