装饰器示例
def w1(func): def inner(): print('...验证权限...') func() return inner @w1 def f1(): print('f1 called') f1()
输出结果
1 ...验证权限... 2 f1 called
装饰器原理
首先,函数 w1(func) 是一个装饰器,该函数有个参数 func,用来接收一个方法;w1 内部又定义了一个函数 inner() ,
执行完 print 之后,调用传进来的函数,然后返回值为内部函数 inner(),这就是 “闭包” 函数。
然后,在 f1 上面的@w1 ,相当于 f1 = w1( f1 ),对其做了简化;此时的 f1 指向了 w1.inner 函数的地址。
两个装饰器执行流程
def makeBold(fun): print('----a----') def inner(): print('----1----') return '<b>' + fun() + '</b>' return inner def makeItalic(fun): print('----b----') def inner(): print('----2----') return '<i>' + fun() + '</i>' return inner @makeBold @makeItalic def test(): print('----c----') print('----3----') return 'hello python decorator' ret = test() print(ret)
执行结果
1 ----b---- 2 ----a---- 3 ----1---- 4 ----2---- 5 ----c---- 6 ----3---- 7 <b><i>hello python decorator</i></b>
首先 test() 先被第二个装饰器(makeItalic)装饰,接着被第一个装饰器(makeBold);而调用过程中,先执行第一个装饰器(makeBold),
然后再执行第二个装饰器(makeItalic)。
接着我们来仔细分析一下:
1、装饰器只对函数进行装饰,不对装饰器装饰。所以在执行@makeBold的时候,遇到另一个装饰器暂停执行;接下来执行@makeItalic,把 test 函数传入
装饰器,从而打印 'b' ,在makeItalic装饰完后,此时 test 指向 makeItalic 的 inner 函数地址;然后 返回来执行 @makeBold ,接着把 新的 test 传入 makeBold
装饰器,因此打印了 'a' 。
2、第一次装饰 test = makeItalic( test ),第二次 装饰 test = makeBold( makeItalic( test ) )。经过分析,此时应该先执行 makeBold. inner 函数,因此先打
印 '1',接下来再调用 makeBold. inner 函数里的 fun,其实就是makeItalic. inner 函数,所以打印 '2',在makeItalic. inner 中调用 fun ,其实就是最原始的 test(),
所以打印 test 函数的 'c','3',最后一层层返回,打印的结果就是 <b><i>hello python decorator</i></b>。
对有参函数进行装饰
指定参数
def w_say(fun): """ 如果原函数有参数,那闭包函数必须保持参数个数一致,并且将参数传递给原方法 """ def inner(name): """ 如果被装饰的函数有行参,那么闭包函数必须有参数 """ print('say inner called') fun(name) return inner @w_say def hello(name): print('hello ' + name) hello('wangcai')
执行结果
1 say inner called 2 hello wangcai
上述代码只能装饰指定参数的函数,下面代码就介绍了如何处理不定长参数
def w_add(func): def inner(*args, **kwargs): print('add inner called') func(*args, **kwargs) return inner @w_add def add(a, b): print('%d + %d = %d' % (a, b, a + b)) @w_add def add2(a, b, c): print('%d + %d + %d = %d' % (a, b, c, a + b + c)) add(2, 4) add2(2, 4, 6)
执行结果
1 add inner called 2 2 + 4 = 6 3 add inner called 4 2 + 4 + 6 = 12
利用python的可变参数轻松实现装饰带参数的函数。
对带返回值的函数进行装饰
下面对有返回值的函数进行装饰,按照之前的写法,代码是这样的
def w_test(func): def inner(): print('w_test inner called start') func() print('w_test inner called end') return inner @w_test def test(): print('this is test fun') return 'hello' ret = test() print('ret value is %s' % ret)
执行结果
1 w_test inner called start 2 this is test fun 3 w_test inner called end 4 ret value is None
从执行结果来看,没有输出 test 函数 'hello',而是 'None';这是因为在 inner 函数中对 test 进行了调用,但没有接收返回值,也没进行返回,所以就是 None。
那么我们修改一下代码
def w_test(func): def inner(): print('w_test inner called start') str = func() print('w_test inner called end') return str return inner @w_test def test(): print('this is test fun') return 'hello' ret = test() print('ret value is %s' % ret)
执行结果
1 w_test inner called start 2 this is test fun 3 w_test inner called end 4 ret value is hello
带参数的装饰器
def func_args(pre='xiaoqiang'): def w_test_log(func): def inner(): print('...记录日志...visitor is %s' % pre) func() return inner return w_test_log # 带有参数的装饰器能够起到在运行时,有不同的功能 # 先执行func_args('wangcai'),返回w_test_log函数的引用 # @w_test_log # 使用@w_test_log对test_log进行装饰 @func_args('wangcai') def test_log(): print('this is test log') test_log()
执行结果
1 ...记录日志...visitor is wangcai 2 this is test log
通过上述代码知道,带参数的装饰器就是在原闭包的基础上又加了一层了闭包。
和两层嵌套相比,三层嵌套执行效果是这样的
test_log = func_args('wangcai')(test_log)
通用装饰器
万能装饰器
def w_test(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner @w_test def test(): print('test called') @w_test def test1(): print('test1 called') return 'python' @w_test def test2(a): print('test2 called and value is %d ' % a) test() test1() test2(9)
执行结果
1 test called 2 test1 called 3 test2 called and value is 9
类装饰器