装饰器示例
def w1(func): def inner(): print('...验证权限...') func() return inner@w1def f1(): print('f1 called')@w1def f2(): print('f2 called')f1()f2()
输出结果:
...验证权限...f1 called...验证权限...f2 called
当调用f1,f2函数时,首先执行了验证。通过一个闭包函数w1,调用函数上通过关键词@w1,对f1,f2完成了装饰。
当python解释器解释@w1时,会调用w1函数,同时将被修饰函数名传入(例如f1),在执行w1函数的时候,直接把inner函数返回了,同事把它赋值给f1,此时的f1已经不是未加修饰的f1了,而是指向了w1.inner函数地址。再调用f1函数,就好先执行权限验证,然后调用原来的f1(),该处的f1是通过参数传进来的f1.
执行时机
def w1(fun): print('...装饰器开始装饰...') def inner(): print('...验证权限...') fun() return inner@w1def test(): print('test')test()
输出结果:
...装饰器开始装饰......验证权限...test
由此可见,执行@w1时相当于执行了如下代码:
test = w1(test)
两个装饰器执行流程和修饰结果
def makeBold(fun): print('----a----') def inner(): print('----1----') return '' + fun() + '' return innerdef makeItalic(fun): print('----b----') def inner(): print('----2----') return '' + fun() + '' return inner@makeBold@makeItalicdef test(): print('----c----') print('----3----') return 'hello python decorator'ret = test()print(ret)
输出结果:
----b--------a--------1--------2--------c--------3----hello python decorator
可以发现,先用第二个装饰器(makeItalic)进行装饰,接着再用第一个装饰器(makeBold)进行装饰,而在调用过程中,先执行第一个装饰器(makeBold),接着再执行第二个装饰器(makeItalic)。
对有参数函数进行装饰
def w_say(fun): """ 如果原函数有参数,那闭包函数必须保持参数个数一致,并且将参数传递给原方法 """ def inner(name): """ 如果被装饰的函数有行参,那么闭包函数必须有参数 :param name: :return: """ print('say inner called') fun(name) return inner@w_saydef hello(name): print('hello ' + name)hello('wangcai')
输出结果:
say inner calledhello wangcai
多个参数或不定个参数
def w_add(func): def inner(*args, **kwargs): print('add inner called') func(*args, **kwargs) return inner@w_adddef add(a, b): print('%d + %d = %d' % (a, b, a + b))@w_adddef add2(a, b, c): print('%d + %d + %d = %d' % (a, b, c, a + b + c))add(2, 4)add2(2, 4, 6)
输出结果:
add inner called2 + 4 = 6add inner called2 + 4 + 6 = 12
对带返回值的函数进行修饰
def w_test(func): def inner(): print('w_test inner called start') func() print('w_test inner called end') return inner@w_testdef test(): print('this is test fun') return 'hello'ret = test()print('ret value is %s' % ret)
输出结果:
w_test inner called startthis is test funw_test inner called endret 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_testdef test(): print('this is test fun') return 'hello'ret = test()print('ret value is %s' % ret)
输出结果:
w_test inner called startthis is test funw_test inner called endret 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()
输出结果:
...记录日志...visitor is wangcaithis is test log
通用装饰器
def w_test(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner@w_testdef test(): print('test called')@w_testdef test1(): print('test1 called') return 'python'@w_testdef test2(a): print('test2 called and value is %d ' % a)test()test1()test2(9)
输出结果:
test calledtest1 calledtest2 called and value is 9
类装饰器
装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在python中,一般callable对象都是函数,但是也有例外。比如只要某个对象重写了call方法,那么这个对象就是callable的。当创建一个对象后,直接去执行这个对象,那么是会抛出异常的,因为他不是callable,无法直接执行,但进行修改后,就可以直接执行调用了,如下
class Test(object): def __call__(self, *args, **kwargs): print('call called')t = Test()print(t())
下面,引入正题,看一下如何用类装饰函数。
class Test(object): def __init__(self, func): print('test init') print('func name is %s ' % func.__name__) self.__func = func def __call__(self, *args, **kwargs): print('装饰器中的功能') self.__func()@Testdef test(): print('this is test func')test()
输出结果:
test initfunc name is test 装饰器中的功能this is test func
和之前的原理一样,当python解释器执行到到@Test时,会把当前test函数作为参数传入Test对象,调用init方法,同时将test函数指向创建的Test对象,那么在接下来执行test()的时候,其实就是直接对创建的对象进行调用,执行其call方法。