Python的装饰器及多层装饰器嵌套函数解析
(2018-09-05 13:57:59)| 分类: 我的研究 |
Python 的装饰器能够在不破坏函数原本结构的基础上,对函数的功能进行补充。当我们需要对一个函数补充不同的功能,可能需要用到多层的装饰器。
def outer(func):
def inner():
print("记录日志开始")
func() # 业务函数
print("记录日志结束")
return inner
def foo():
print("foo")
foo = outer(foo)
foo()
没有修改 foo 函数里面的任何逻辑,只是给 foo 变量重新赋值了,指向了一个新的函数对象,最后调用 foo(),不仅能打印日志,业务逻辑(打印foo)也执行完了,现在来分析一下它的执行流程:
这里的 outer 函数其实就是一个装饰器,装饰器是一个带有函数作为参数并返回一个新函数的闭包,本质上装饰器也是函数。outer 函数的返回值是 inner 函数,在 inner 函数中,除了执行日志操作,还有业务代码,该函数(inner)重新赋值给 foo 变量后,调用 foo() 就相当于调用 inner()。
另外,Python为装饰器提供了语法糖 @,如上语句可以简化成:
def outer(func):
def inner():
print("记录日志开始")
func() # 业务函数
print("记录日志结束")
return inner
@outer
def foo():
print("foo")
foo()
多层装饰器:
def D(funA):
def decorated_A(funB):
def decorated_B(funC):
def decorated_C_by_BAD(str1):
out = funA(funB)(funC)(str1)
return out +' > decorated by D'
return decorated_C_by_BAD
return decorated_B
return decorated_A
@D
def A(funB):
def decorated_B(funC):
def decorated_C_by_BA(str1):
out = funB(funC)(str1)
return out +' > decorated by A'
return decorated_C_by_BA
return decorated_B
@A
def B(funC):
def decorated_C_by_B(str2):
return funC(str2)+' > decorated by B'
return decorated_C_by_B
@B
def C(str3):
return str3
print(C('SHOW THE FIRST'))
结果:
SHOW THE FIRST > decorated by B > decorated by A > decorated by D
1层装饰@B,装饰程序B要2层嵌套B(C)(str3),用B去装饰(添加语句> decorated by B),B(C)=decorated_C_by_B,B(C)(str3)=decorated_C_by_B(str3)= return C(str3)+' > decorated by B'。
2层装饰@A,装饰程序A要3层嵌套A(B)(C)(str3),然后B用A去装饰(添加语句> decorated by A),A(B)= decorated_B,A(B)(C)= decorated_C_by_BA,A(B)(C)(str3)= "out = funB(funC)(str1)"= "out = B(C)(str3)"。
3层装饰@D,装饰程序要4层嵌套funA(funB)(funC)(str1),
说明:每加1层装饰,外层装饰程序就要比内层装饰程序多嵌套1层,嵌套的每层程序就是用来接收内层装饰的函数。
比如:
def A(funB):
外层装饰函数A用来接收内层装饰函数B,嵌套函数decorated_B用来接收装饰函数B的内层函数C,decorated_C_by_BA用来接收最内层函数C的变量str。
def A(funE_decorated_by_C):
def redecorated_E(str):
return funE_decorated_by_C(str)+' > redecorated by A'
return redecorated_E
def C(funE):
def decorated_E(str):
return funE(str)+' > decorated by C'
return decorated_E
@A
@C
def E(str):
return str
print (E('A string is '))
结果:
A string is
这种情况下,E(str) = A(C(E(str)))。首先装饰器 C 装饰函数 E,返回一个被 C 装饰过的函数,然后装饰器 A 再装饰这个被 C 装饰过的函数。与第一种情况的区别是,这里的装饰器 A 装饰的是一个函数,而不是一个装饰器。
多个装饰器的调用,装饰器函数自身运行的顺序是自内而外,嵌套子程序的运行顺序由外向内!!!
def decorator_a(func):
print ('Get in decorator_a')
def inner_a(*args, **kwargs):
print ('Get in inner_a')
return func(*args, **kwargs)
return inner_a
def decorator_b(func):
print ('Get in decorator_b')
def inner_b(*args, **kwargs):
print ('Get in inner_b')
return func(*args, **kwargs)
return inner_b
@decorator_b
@decorator_a
def f(x):
print ('Get in f')
return x * 2
f(1)
结果:
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f
层次(由上而下):b(a)(f)(x),b装饰a,a装饰f,所以主函数先调用内层a,执行a里面的print,再调用外层b,执行b里面的print,所以首先打印出来Get in decorator_a,Get in decorator_b。
然后执行外层装饰b里面的嵌套inner_b,打印然后返回func(其实这个func就类似于一个func(inner_a)),由func指向去执行内层装饰a里面的嵌套inner_a,inner_a打印后,指向func,这时func=func(f)(x),打印最后的Get in f。
下面的程序更加清晰说明这个问题:
def decorator2(func):
print('内层装饰主程序')
def wrapper2():
func()
('内层装饰嵌套')
return wrapper2
def decorator1(func):
print("外层装饰主程序")
def wrapper1():
func()
('外层装饰嵌套')
return wrapper1
@decorator1
@decorator2
def test():
('hello python!')
test()
结果:
内层装饰主程序
外层装饰主程序
hello python!
内层装饰嵌套
外层装饰嵌套
结果说明:先执行内层主程序--》外层主程序。
再执行外层装饰器的嵌套函数:由于嵌套函数先执行func(),再执行print,但是func()在这里由于再次嵌套了一个内层装饰器,即func()=func(内层嵌套函数),所以此时func()相当于先转去执行了内层嵌套函数,内层嵌套函数的第一句也是func(),但此时的func()所带的参数就直接是f(x),f(x)不是装饰器,也没有嵌套函数,所以直接执行f(x)的结果:hello python!,然后打印内层嵌套print,最后才执行外层嵌套的print。
如果装饰器带参数,那么需要再嵌套一层:
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print("after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print(" myfunc() called.")
myfunc()
按理1层装饰,2层嵌套即可,但是deco带了参数,所以必须再加1层。1层用来接收deco的参数,1层用来接收执行函数,1层用来接收执行函数的参数。

加载中…