献给莹莹
1.什么是装饰器
以下为装饰器例子
def w1(func):
def inner():
print('...验证权限...')
func()
return inner
@w1
def f1():
print('f1 called')
@w1
def f2():
print('f2 called')
f1()
f2()
该程序的输出为:
...验证权限...
f1 called
...验证权限...
f2 called
- 首先,来看我们的装饰器函数w1,该函数接收一个参数func,其实就是接收一个方法名,w1内部又定义一个函数inner,在inner函数中增加权限校验,并在验证完权限后调用传进来的参数func,同时w1的返回值为内部函数inner,其实就是一个闭包函数。
- 然后,再来看一下,在f1上增加@w1,那这是什么意思呢?当python解释器执行到这句话的时候,会去调用w1函数,同时将被装饰的函数名作为参数传入(此时为f1),根据闭包一文分析,在执行w1函数的时候,此时直接把inner函数返回了,同时把它赋值给f1,此时的f1已经不是未加装饰时的f1了,而是指向了w1.inner函数地址。
- 接下来,在调用f1()的时候,其实调用的是w1.inner函数,那么此时就会先执行权限验证,然后再调用原来的f1(),该处的f1就是通过装饰传进来的参数f1。
- 这样下来,就完成了对f1的装饰,实现了权限验证。
实际上python执行到@w1时,就已经开始装饰,相当于执行了:
f1=w1(f1)
2.两个装饰器的执行流程和装饰结果
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)
输出结果:
----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>
- 以上程序较为复杂,可以这样理解
- 1.装饰时机 通过上面装饰时机的介绍,我们可以知道,在执行到@makeBold的时候,需要对下面的函数进行装饰,此时解释器继续往下走,发现并不是一个函数名,而又是一个装饰器,这时候,@makeBold装饰器暂停执行,而接着执行接下来的装饰器@makeItalic,接着把test函数名传入装饰器函数,在makeItalic装饰完后,此时的test指向makeItalic的inner函数地址,这时候有返回来执行@makeBold,接着把新test传入makeBold装饰器函数中。
@makeItalic 等价于 test=makeItalic(test)
@makeBold 等价于 test=makeBold(makeItalic.inner)
3.通用装饰器----对无参函数和有参函数进行装饰的区别
- 上面例子中都是对无参函数的装饰,不再单独举例
- 对有参数函数装饰
def w_say(fun):
def inner(name):
print('say inner called')
fun(name)
return inner
@w_say
def hello(name):
print('hello ' + name)
只需要修改inner即可。
对于多个或不定长参数时,可以参考如下:
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))
如果函数有返回值,在inner内返回即可
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'
如果装饰器本身需要参数,可以这样写:
def func_args(pre=None):
def w_test_log(func):
def inner():
print('...记录日志...visitor is %s' % pre)
func()
return inner
return w_test_log
@func_args('yingying')
def test_log():
print('this is test log')
即在外嵌套一层函数
至此,我们就可以引出通用的装饰器写法了
def w1(pre=None):
def w1_test(func):
def inner(*args,**kwargs):
print("{} is now using the system".format(pre))
ret=func(*args,**kwargs)
return ret
return inner
return w1_test
@w1("icey")
def test_log(a,b,c,d=True):
res = a + b
if d:
print(a+b+c)
return res
以上程序输出结果
icey is now using the system
9
4.类装饰器
结合类的特性来说,类可以记录数据(属性),而函数不行(闭包某种意义上也可行),利用这种特性可以实现基于类的装饰器
1
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('装饰器中的功能')
return self.__func()
@Test
def test():
print('this is test func')
test()
输出结果为:
test init
func name is test
装饰器中的功能
this is test func
2
class Counter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
return self.func(*args, **kwargs)
@Counter
def foo():
pass
for i in range(10):
foo()
print(foo.count)
实际上相当于执行了foo=Counter(foo()),并执行了__call__
内的内容。
网友评论