这是一篇迟到的总结。
以下是我的学习总结步骤:
-
STEP1:
# test1 begin:
# 不是装饰器的装饰器
def deco1(func):
print "before func() called:"
func()
print "after func() called."
return func
@deco1
def run1():
print "run1() called!"
return
# test1 end;
"""
before func() called.
run1() called.
after func() called.
run1() called.
"""
执行完run1()方法后可以看到run1()函数被执行了两次,并不符合要求,所以我称之为不是装饰器的装饰器。严格意义上它也是个装饰器,哈哈。
-
STEP2:
# test2 begin:
# 最简易的装饰器,在函数执行前后附加操作
def deco2(func):
def _deco():
print "before func() called:"
print func()
print "after func() called."
return _deco
@deco2
def run2():
print "run2() called!"
return "OK"
# test2 end;
"""
before func() called:
run2() called!
OK
after func() called.
"""
这就是一个最简易的装饰器,它完成了装饰器应该完成的任务,但是它只能装饰不带参数的函数。
-
STEP3:
# test3 begin:
# 对带固定参数的函数进行装饰
def deco3(func):
def _deco(a, b):
print "before func() called:"
res = func(a, b)
print "after func() called."
return res
return _deco
@deco3
def run3(a, b):
print "run3(%s, %s) called!" % (a, b)
return a + b
# test3 end;
"""
before func() called:
run3(1, 3) called!
after func() called.
4
"""
步骤三能够达到装饰带参数的函数的效果了,但是函数参数如果不一样呢?
-
STEP4:
# test4 begin:
# 对带随机参数的函数进行装饰
def deco4(func):
def _deco(*a, **b):
print "before func() called:"
res = func(*a, **b)
print "after func() called."
return res
return _deco
@deco4
def run4_1(a, b):
print "run4_1(%s, %s) called!" % (a, b)
return a + b
@deco4
def run4_2(a, b, c):
print "run4_2(%s, %s, %s) called!" % (a, b, c)
return a + b + c['+'] - c['-']
# test4 end;
"""
before func() called:
run4_1(1, 2) called!
after func() called.
3
before func() called:
run4_2(3, 5, {'+': 2, '-': 10}) called!
after func() called.
0
"""
到这里,被装饰的函数的参数个数就是可以变化的了。
-
STEP5:
# test5 begin:
# 让装饰器带参数
def deco5(arg):
def _deco(func):
def __deco(*a, **b):
print "[%s]before %s called:" % (arg, func.__name__)
res = func(*a, **b)
print "[%s]after %s called." % (arg, func.__name__)
return res
return __deco
return _deco
@deco5("test5_1")
def run5_1(a, b):
print "run5_1(%s, %s) called!" % (a, b)
return a + b
@deco5("test5_2")
def run5_2(a, b, c):
print "run5_2(%s, %s, %s) called!" % (a, b, c)
return a + b + c['+'] - c['-']
# test5 end;
"""
[test5_1]before run5_1 called:
run5_1(1, 2) called!
[test5_1]after run5_1 called.
3
[test5_2]before run5_2 called:
run5_2(3, 5, {'+': 2, '-': 10}) called!
[test5_2]after run5_2 called.
0
"""
为了让装饰器本身可以带参数,我们把装饰器的定义结构做了改变,可以看到是三层嵌套的,最外层的入参就是装饰器自己的参数,中间层的入参是被装饰的函数句柄,而最内层的函数入参就是被装饰的函数的入参了。
-
STEP6:
# test6 begin:
# 让装饰器带类参数
def deco6(cls):
def _deco(func):
def __deco(*a, **b):
print "before %s called:" % (func.__name__)
cls.before()
res = func(*a, **b)
cls.after()
print "after %s called." % (func.__name__)
return res
return __deco
return _deco
class Deco6(object):
def __init__(self):
print "Deco6.__init__() called."
@classmethod
def before(cls):
print "Deco6.before() called:"
@classmethod
def after(cls):
print "Deco6.after() called;"
@deco6(Deco6)
def run6_1(a, b):
print "run6_1(%s, %s) called!" % (a, b)
return a + b
@deco6(Deco6)
def run6_2(a, b, c):
print "run6_2(%s, %s, %s) called!" % (a, b, c)
return a + b + c['+'] - c['-']
# test7 end;
"""
before run6_1 called:
Deco6.before() called:
run6_1(1, 2) called!
Deco6.after() called;
after run6_1 called.
3
before run6_2 called:
Deco6.before() called:
run6_2(3, 5, {'+': 2, '-': 10}) called!
Deco6.after() called;
after run6_2 called.
0
"""
至于带类参数,道理上是一样的,其实字符串参数本身就是类对象,类本身也是对象。
-
STEP7:
# test7 begin:
# 让装饰器来装饰装饰器
@deco5("test7")
@deco6(Deco6)
def run7(a, b, c):
print "run7_2(%s, %s, %s) called!" % (a, b, c)
return a + b + c['+'] - c['-']
# test7 end;
"""
[test7]before __deco called:
before run7 called:
Deco6.before() called:
run7_2(8, 5, {'+': 2, '-': 10}) called!
Deco6.after() called;
after run7 called.
[test7]after __deco called.
5
(有双横线会变成粗体,所以上面这段输出我当做代码贴上来了。)这里主要测试一下装饰器的嵌套,至于效果,读者自行根据执行结果总结哈。
下面是我的执行过程,如果对上面输出结果的最后一行数字有疑惑,可以看看我下面的代码,有print
的就会多出一行。
if __name__ == '__main__':
# run1()
# run2()
# print run3(1, 3)
# print run4_1(1, 2)
# print run4_2(3, 5, {'-': 10, '+': 2})
# print run5_1(1, 2)
# print run5_2(3, 5, {'-': 10, '+': 2})
# print run6_1(1, 2)
# print run6_2(3, 5, {'-': 10, '+': 2})
# print run7(8, 5, {'-': 10, '+': 2})