加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

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,装饰程序B2层嵌套B(C)(str3),用B去装饰(添加语句> decorated by B),B(C)=decorated_C_by_BB(C)(str3)=decorated_C_by_B(str3)= return C(str3)+' > decorated by B'

 

2层装饰@A,装饰程序A3层嵌套A(B)(C)(str3),然后BA去装饰(添加语句> decorated by A),A(B)= decorated_BA(B)(C)= decorated_C_by_BAA(B)(C)(str3)= "out = funB(funC)(str1)"= "out = B(C)(str3)"

 

3层装饰@D,装饰程序要4层嵌套funA(funB)(funC)(str1)

 

说明:每加1层装饰,外层装饰程序就要比内层装饰程序多嵌套1层,嵌套的每层程序就是用来接收内层装饰的函数。

比如:

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用来接收内层装饰函数B,嵌套函数decorated_B用来接收装饰函数B的内层函数Cdecorated_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  > decorated by C > redecorated by A

这种情况下,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装饰aa装饰f,所以主函数先调用内层a,执行a里面的print,再调用外层b,执行b里面的print,所以首先打印出来Get in decorator_aGet in decorator_b

然后执行外层装饰b里面的嵌套inner_b,打印然后返回func(其实这个func就类似于一个funcinner_a)),由func指向去执行内层装饰a里面的嵌套inner_ainner_a打印后,指向func,这时func=func(f)(x),打印最后的Get in f

下面的程序更加清晰说明这个问题:

def decorator2(func):
   
print('内层装饰主程序')
   
def wrapper2():
        func()
       
print         ('内层装饰嵌套')
   
return wrapper2

def decorator1(func):
   
print("外层装饰主程序")
   
def wrapper1():
        func()
       
print         ('外层装饰嵌套')
   
return wrapper1

@decorator1
@decorator2

def test():
   
print     ('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层用来接收执行函数的参数。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有