Python装饰器学习(九步入门) 分类: python学习 2015-03-06 14:26 90人阅读 评论(0) 收藏

原文网址:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html



这是在Python学习小组上介绍的内容,现学现卖、多练习是好的学习方式。

第一步:最简单的函数,准备附加额外功能

1
2
3
4
5
6
7
8
# -*- coding:gbk -*-
'''示例1: 最简单的函数,表示调用了两次'''
 
def myfunc():
    print("myfunc() called.")
 
myfunc()
myfunc()

 

第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding:gbk -*-
'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''
 
def deco(func):
    print("before myfunc() called.")
    func()
    print("  after myfunc() called.")
    returnfunc
 
def myfunc():
    print(" myfunc() called.")
 
myfunc =deco(myfunc)
 
myfunc()
myfunc()

第三步:使用语法糖@来装饰函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding:gbk -*-
'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''
 
def deco(func):
    print("before myfunc() called.")
    func()
    print("  after myfunc() called.")
    returnfunc
 
@deco
def myfunc():
    print(" myfunc() called.")
 
myfunc()
myfunc()

第四步:使用内嵌包装函数来确保每次新函数都被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding:gbk -*-
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
 
def deco(func):
    def_deco():
        print("before myfunc() called.")
        func()
        print("  after myfunc() called.")
        # 不需要返回func,实际上应返回原函数的返回值
    return_deco
 
@deco
def myfunc():
    print(" myfunc() called.")
    return'ok'
 
myfunc()
myfunc()

第五步:对带参数的函数进行装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding:gbk -*-
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
 
def deco(func):
    def_deco(a, b):
        print("before myfunc() called.")
        ret= func(a, b)
        print("  after myfunc() called. result: %s"% ret)
        returnret
    return_deco
 
@deco
def myfunc(a, b):
    print(" myfunc(%s,%s) called."% (a, b))
    returna + b
 
myfunc(1,2)
myfunc(3,4)

第六步:对参数数量不确定的函数进行装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# -*- coding:gbk -*-
'''示例6: 对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''
 
def deco(func):
    def_deco(*args,**kwargs):
        print("before %s called."% func.__name__)
        ret= func(*args,**kwargs)
        print("  after %s called. result: %s"% (func.__name__, ret))
        returnret
    return_deco
 
@deco
def myfunc(a, b):
    print(" myfunc(%s,%s) called."% (a, b))
    returna+b
 
@deco
def myfunc2(a, b, c):
    print(" myfunc2(%s,%s,%s) called."% (a, b, c))
    returna+b+c
 
myfunc(1,2)
myfunc(3,4)
myfunc2(1,2, 3)
myfunc2(3,4, 5)

第七步:让装饰器带参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding:gbk -*-
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
 
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.")
 
@deco("module2")
def myfunc2():
    print(" myfunc2() called.")
 
myfunc()
myfunc2()

第八步:让装饰器带 类 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- coding:gbk -*-
'''示例8: 装饰器带类参数'''
 
class locker:
    def__init__(self):
        print("locker.__init__() should be not called.")
         
    @staticmethod
    defacquire():
        print("locker.acquire() called.(这是静态方法)")
         
    @staticmethod
    defrelease():
        print("  locker.release() called.(不需要对象实例)")
 
def deco(cls):
    '''cls 必须实现acquire和release静态方法'''
    def_deco(func):
        def__deco():
            print("before %s called [%s]."% (func.__name__, cls))
            cls.acquire()
            try:
                returnfunc()
            finally:
                cls.release()
        return__deco
    return_deco
 
@deco(locker)
def myfunc():
    print(" myfunc() called.")
 
myfunc()
myfunc()

第九步:装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# -*- coding:gbk -*-
'''mylocker.py: 公共类 for 示例9.py'''
 
class mylocker:
    def__init__(self):
        print("mylocker.__init__() called.")
         
    @staticmethod
    defacquire():
        print("mylocker.acquire() called.")
         
    @staticmethod
    defunlock():
        print("  mylocker.unlock() called.")
 
class lockerex(mylocker):
    @staticmethod
    defacquire():
        print("lockerex.acquire() called.")
         
    @staticmethod
    defunlock():
        print("  lockerex.unlock() called.")
 
def lockhelper(cls):
    '''cls 必须实现acquire和release静态方法'''
    def_deco(func):
        def__deco(*args,**kwargs):
            print("before %s called."% func.__name__)
            cls.acquire()
            try:
                returnfunc(*args,**kwargs)
            finally:
                cls.unlock()
        return__deco
    return_deco
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding:gbk -*-
'''示例9: 装饰器带类参数,并分拆公共类到其他py文件中
同时演示了对一个函数应用多个装饰器'''
 
from mylocker import *
 
class example:
    @lockhelper(mylocker)
    defmyfunc(self):
        print(" myfunc() called.")
 
    @lockhelper(mylocker)
    @lockhelper(lockerex)
    defmyfunc2(self, a, b):
        print(" myfunc2() called.")
        returna + b
 
if __name__=="__main__":
    a= example()
    a.myfunc()
    print(a.myfunc())
    print(a.myfunc2(1,2))
    print(a.myfunc2(3,4))

下面是参考资料,当初有不少地方没看明白,真正练习后才明白些:

1. Python装饰器学习 http://blog.csdn.net/thy38/article/details/4471421

2. Python装饰器与面向切面编程 http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html

3. Python装饰器的理解 http://apps.hi.baidu.com/share/detail/17572338


作者追加评论:

1、对于装饰函数的最里层函数,最好再加上“@functools.wraps(func)”的修饰,这样就能保留原函数的特殊属性,例如:

import functools

def lockhelper(cls):
def _deco(func):
@functools.wraps(func)
def __deco(*args, **kwargs):

2、以上例子都是对每个函数单独进行包装,在待包装函数之前都有一个@,是否可以不在每个函数前都加修饰呢?下面这篇博文的末尾处介绍了一种自动遍历类的每个方法,自动为每个方法添加装饰:
http://blog.csdn.net/zylcf818/article/details/5342276


我对自动批量添加装饰进行了试验,可支持多个装饰器,效果挺好:http://www.cnblogs.com/rhcad/archive/2011/12/21/2296063.html



****************************

后记:看到一个词:语法糖,然后查到python的装饰器。看到本文,通过debug代码,稍微理解了正文中各部方式,不过刚入门

以下是我测试的代码的输出:


myfunc() called.
myfunc() called.


before myfunc() called.
 myfunc() called.
  after myfunc() called.
 myfunc() called.
 myfunc() called.


before myfunc() called.
 myfunc() called.
  after myfunc() called.
 myfunc() called.
 myfunc() called.


before myfunc() called.
 myfunc() called.
  after myfunc() called.
before myfunc() called.
 myfunc() called.
  after myfunc() called.

五(多的xxxx是以为我在myfun的返回之前加的一句print 'xxxx')

before myfunc() called.
 myfunc(1,2) called.
xxxx
  after myfunc() called. result: 3
before myfunc() called.
 myfunc(3,4) called.
xxxx
  after myfunc() called. result: 7

六(其中deco中的‘return ret’注释掉好像没有影响)

before myfunc called.
 myfunc(1,2) called.
  after myfunc called. result: 3
before myfunc called.
 myfunc(3,4) called.
  after myfunc called. result: 7
before myfunc2 called.
 myfunc2(1,2,3) called.
  after myfunc2 called. result: 6
before myfunc2 called.
 myfunc2(3,4,5) called.
  after myfunc2 called. result: 12


before myfunc called [mymodule].
 myfunc() called.
  after myfunc called [mymodule].
before myfunc2 called [module2].
 myfunc2() called.
  after myfunc2 called [module2].

八(没有打印文中的汉字是因为之前中文报错,改成英语的)

before myfunc called [__main__.locker].
locker.acquire() called.(This is static method)
 myfunc() called.
  locker.release() called.(No need Object instance)
before myfunc called [__main__.locker].
locker.acquire() called.(This is static method)
 myfunc() called.
  locker.release() called.(No need Object instance)

九(这一步的前半段是写在mylocker.py中的,其中class lockerex(mylocker):改成class lockerex():好像对结果不造成影响。这里应该表示两个类的相互继承的意思吧)

before myfunc called.
mylocker.acquire() called.
 myfunc() called.
  mylocker.unlock() called.
before myfunc called.
mylocker.acquire() called.
 myfunc() called.
  mylocker.unlock() called.
None
before __deco called.
mylocker.acquire() called.
before myfunc2 called.
lockerex.acquire() called.
 myfunc2() called.
  lockerex.unlock() called.
  mylocker.unlock() called.
3
before __deco called.
mylocker.acquire() called.
before myfunc2 called.
lockerex.acquire() called.
 myfunc2() called.
  lockerex.unlock() called.
  mylocker.unlock() called.
7

注:这些结果可能与原结果有些不同,因为中间我可能有些小小的改动,懒得去查了。还有代码中有些return是估计无用的。本来一边学一边记可能会加些笔记,现在只能加些结果了。

posted @ 2015-03-06 14:26  冰菓折木  阅读(90)  评论(0编辑  收藏  举报