这篇文章也写得非常好,参看:http://www.wklken.me/posts/2013/07/19/python-translate-decorator.html
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
>>> def now():
... print '2013-12-25'
...
>>> f = now
>>> f()
2013-12-25
函数对象有一个__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
a
+
' love '
+
b
return
a
+
' 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
a
+
b
if
__name__
=
=
"__main__"
:
a
=
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>
|