Python装饰器 Decorator

原文链接

这篇文章也写得非常好,参看:http://www.wklken.me/posts/2013/07/19/python-translate-decorator.html

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

>>> def now():
...     print '2013-12-25'
...
>>> f = now
>>> f()
2013-12-25

 
 
Try

函数对象有一个__name__属性,可以拿到函数的名字:

>>> now.__name__
'now'
>>> f.__name__
'now'

现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
    print '2013-12-25'

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

>>> now()
call now():
2013-12-25

@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print '%s %s():' % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

这个3层嵌套的decorator用法如下:

@log('execute')
def now():
    print '2013-12-25'

执行结果如下:

>>> now()
execute now():
2013-12-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log('execute')(now)

我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

或者针对带参数的decorator:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print '%s %s():' % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

小结

在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。

decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。


参考:http://my.oschina.net/shniu/blog/215365?p=1

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

        动态地给一个对象添加一些额外的职责。    

        模式的好处:认证,权限检查,记日志,检查参数,加锁,等等等等,这些功能和系统业务无关,但又是系统所必须的,说的更明白一点,就是面向方面的编程(AOP)。AOP把与业务无关的代码十分干净的从系统中切割出来 。

1、入门

?
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
37
38
39
def  bar():
     print  'Entry the door example...'
bar()
# 突然想要计算这个函数的运行时间。如下:
import  time
def  bar():
     start  =  time.clock()
     print  'Entry the door example...'
     end  =  time.clock()
     print  'used:' , end  -  start
bar()
 
# 很好,功能实现;变态的事情来了,项目经理说,那里有一堆函数,我想知道那一堆函数的运行时间;我去,肿么搞,每个函数都复制一套代码??不好。google一下,如下:
import  time
def  bar():
     print  'Entry the door example...'
 
def  timer(func):
     start  =  time.clock()
     func()
     end  =  time.clock()
     print  'used:' , end  -  start
 
timer(bar)
# 看了下,凑活,不太好;看下面:
import  time
def  timer(func):
     def  wrap():
         start  =  time.clock()
         func()
         end  =  time.clock()
         print  'used:' , end  -  start
     return  wrap
 
 
def  bar():
     print  'Entry the door example...'
bar  =  timer(bar)
bar()

2、python对装饰器模式的支持

    Python内置了对装饰器模式的支持。基本的语法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@decorator
def  bar():
     pass
# 其中,装饰器A可以是类,也可以是函数。同时,装饰器A和函数a也可以带有各自的参数。此外,还可以将多个装饰器加载到同一个对象上,多个装饰器将按顺序依次装饰该对象。
 
# 将让面的例子,改造一下
import  time
def  timer(func):
     def  wrap():
         start  =  time.clock()
         func()
         end  =  time.clock()
         print  'used:' , end  -  start
     return  wrap
 
@timer
def  bar():
     print  'Entry the door example...'
bar()
# 关注@timer,在定义上加上这一行与另外写bar = timer(bar)完全等价,千万不要以为@有另外的魔力。除了字符输入少了一些,还有一个额外的好处:这样看上去更有装饰器的感觉。

3、python内置的装饰器

    内置的装饰器有三个,分别是staticmethod、classmethod和property,作用分别是把类中定义的实例方法变成静态方法、类方法和类属性。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  Rabbit( object ):
      
     def  __init__( self , name):
         self ._name  =  name
      
     @ staticmethod
     def  newRabbit(name):
         return  Rabbit(name)
      
     @ classmethod
     def  newRabbit2( cls ):
         return  Rabbit('')
      
     @ property
     def  name( self ):
         return  self ._name
     @name.setter
     def  name( self , name):
         self ._name  =  name

    functools模块提供了两个装饰器。这个模块是Python 2.5后新增的。

    wraps(wrapped[, assigned][, updated]): 
    这是一个很有用的装饰器。函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名bar会变成包装函数的名字wrap,如果你希望使用反射,可能会导致意外的结果。这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import  time
import  functools
def  timer(func):
     @functools.wraps(func)
     def  wrap():
         start  =  time.clock()
         func()
         end  =  time.clock()
         print  'used:' , end  -  start
     return  wrap
 
@timer
def  bar():
     print  'Entry the door example...'
bar()
print  bar.__name__

?
1
http: //www .cnblogs.com /huxi/archive/2011/03/01/1967600 .html

    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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# 对带参数的函数进行装饰
def  params(func):
'''对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
     def  _wrap(args1, args2):
         print  'called before...'
         func(args1, args2)
         print  'called after...'
     return  _wrap
 
@params
def  hello(boy, girl):
     print  boy  +  ' and '  +  girl  +  ' will 1314.'
 
hello( 'niuyakun' 'huruili' )
 
# 对参数数量不确定的函数进行装饰
def  forever(func):
'''对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''
     def  _forever( * args,  * * kwargs):
         print  ( 'before %s called.'  %  func.__name__)
         ret  =  func( * args,  * * kwargs)
         print  ( 'after %s called.result: %s.'  %  (func.__name__, ret))
     return  _forever
 
@forever
def  love(a, b):
     print  +  ' love '  +  b
     return  +  ' love '  +  b
love( 'niuyakun' 'huruili' )
 
# 让装饰器带参数
def  lv(args):
'''让装饰器带参数,和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
     print  args
     def  _lv(func):
         def  __lv( * args):
             print  'before %s called.'  %  func.__name__
             func( * args)
             print  'after %s called.'  %  func.__name__
         return  __lv
     return  _lv
 
@lv ( 'ok' )
def  lo(a):
     print  'called %s'  %  a
 
lo( 'haha' )
 
# 让装饰器带 类 参数
class  locker:
     def  __init__( self ):
         print ( "locker.__init__() should be not called." )
          
     @ staticmethod
     def  acquire():
         print ( "locker.acquire() called.(这是静态方法)" )
          
     @ staticmethod
     def  release():
         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 :
                 return  func()
             finally :
                 cls .release()
         return  __deco
     return  _deco
  
@deco (locker)
def  myfunc():
     print ( " myfunc() called." )
  
myfunc()
myfunc()
# 对于装饰函数的最里层函数,最好再加上“@functools.wraps(func)”的修饰,这样就能保留原函数的特殊属性,例如:
 
         import  functools
         
         def  lockhelper( cls ):
             def  _deco(func):
                 @functools.wraps(func)
                 def  __deco( * args,  * * kwargs):
# 运行结果:
before myfunc called [__main__.locker].
locker.acquire() called.(这是静态方法)
  myfunc() called.
   locker.release() called.(不需要对象实例)
before myfunc called [__main__.locker].
locker.acquire() called.(这是静态方法)
  myfunc() called.
   locker.release() called.(不需要对象实例)
   
# 装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器
dec.py:
class  mylocker:
     def  __init__( self ):
         print ( "mylocker.__init__() called." )
          
     @ staticmethod
     def  acquire():
         print ( "mylocker.acquire() called." )
          
     @ staticmethod
     def  unlock():
         print ( "  mylocker.unlock() called." )
  
class  lockerex(mylocker):
     @ staticmethod
     def  acquire():
         print ( "lockerex.acquire() called." )
          
     @ staticmethod
     def  unlock():
         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 :
                 return  func( * args,  * * kwargs)
             finally :
                 cls .unlock()
         return  __deco
     return  _deco
     
main.py:
from  mylocker  import  *
  
class  example:
     @lockhelper(mylocker)
     def  myfunc( self ):
         print ( " myfunc() called." )
  
     @lockhelper(mylocker)
     @lockhelper(lockerex)
     def  myfunc2( self , a, b):
         print ( " myfunc2() called." )
         return  +  b
  
if  __name__ = = "__main__" :
     =  example()
     a.myfunc()
     print (a.myfunc())
     print (a.myfunc2( 1 2 ))
     print (a.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
25
26
27
28
# 别人写的
def  synchronized(lock):
     """锁同步装饰方法
     !lock必须实现了acquire和release方法
     """
     def  sync_with_lock(func):
         def  new_func( * args,  * * kwargs):
             lock.acquire()
             try :
                 return  func( * args,  * * kwargs)
             finally :
                 lock.release()
         new_func.func_name  =  func.func_name
         new_func.__doc__  =  func.__doc__
         return  new_func
     return  sync_with_lock
@synchronized (__locker)
def  update(data):
"""更新计划任务"""
     tasks  =  self .get_tasks()
     delete_task  =  None
     for  task  in  tasks:
         if  task[PLANTASK. ID = =  data[PLANTASK. ID ]:
             tasks.insert(tasks.index(task), data)
             tasks.remove(task)
             delete_task  =  task
     r, msg  =  self ._refresh(tasks, delete_task)
     return  r, msg, data[PLANTASK. ID ]

?
1
2
3
4
http: //fity .info /2013/12/12/py-decorator/
http: //my .oschina.net /leejun2005/blog/146025
http: //www .oschina.net /translate/dry-principles-through-python-decorators
http: //blog .sae.sina.com.cn /archives/3151
?
1
<a href= "http://www.cnblogs.com/huxi/archive/2011/03/31/2001522.html" rel= "nofollow" >http: //www .cnblogs.com /huxi/archive/2011/03/31/2001522 .html<br>http: //www .cnblogs.com /huxi/archive/2011/01/02/1924317 .html< /a > <br><br> <br>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值