在《Core Python Programming 2nd》中学习到了装饰器,这对我来说是个完全陌生的语法,第一遍愣是没看懂,很有必要记一下。
第一眼看到这个词Decorator,我联想到了DP中的Decorator模式,后来才知道完全不是这么一回事。(再次鄙视一下国内浮躁的博客,我google了一下,几乎千篇一律都是什么锁同步装饰器、超时装饰器,我对原作者表达敬仰,可是大家都是转载就不像话了,也是对网络资源的极大浪费,也许真正有价值的博文就湮没在这片都是一模一样的东西里了)
1. 这是个什么东东?
书上说得不是太清楚,它说类似于Java的AOP(Aspect Oriented Programming,面向方面编程),我对AOP一无所知。根据我对许多例子用法的反复揣摩,我认为是类似于程序设计语义学中所说的前键 与后键 的概念(Eiffel中的@pre 与@post )。当然你可以在装饰器中做比前键与后键更多的事,如:引入日志、增加计时逻辑来检测性能、给函数增加事务的能力。
其实总体说起来,装饰器其实也就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。
2. 装饰器语法
(1)无参数装饰器
- def deco(func):
- print func
- return func
- @deco
- def foo():pass
- foo()
第一个函数deco是装饰函数,它的参数就是被装饰的函数对象。我们可以在deco函数内对传入的函数对象做一番“装饰”,然后返回这个对象(记住一定要返回 ,不然外面调用foo的地方将会无函数可用。实际上此时foo=deco(foo))
我写了个小例子,检查函数有没有说明文档:
- def deco_functionNeedDoc(func):
- if func.__doc__ == None :
- print func, "has no __doc__, it's a bad habit."
- else:
- print func, ':', func.__doc__, '.'
- return func
- @deco_functionNeedDoc
- def f():
- print 'f() Do something'
- @deco_functionNeedDoc
- def g():
- 'I have a __doc__'
- print 'g() Do something'
- f()
- g()
(2)有参数装饰器
- def decomaker(arg):
- '通常对arg会有一定的要求'
- """由于有参数的decorator函数在调用时只会使用应用时的参数
- 而不接收被装饰的函数做为参数,所以必须在其内部再创建
- 一个函数
- """
- def newDeco(func): #定义一个新的decorator函数
- print func, arg
- return func
- return newDeco
- @decomaker(deco_args)
- def foo():pass
- foo()
第一个函数decomaker是装饰函数,它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象,所以在内部必须至少创建一个接受被装饰函数的函数,然后返回这个对象(实际上此时foo=decomaker(arg)(foo))
这个我还真想不出什么好例子,还是见识少啊,只好借用同步锁的例子了:
- def synchronized(lock):
-
-
-
- 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]
调用时还是updae(data)。
同时还可以将多个装饰器组合 使用:
- @synchronized(__locker)
- @deco_functionNeedDoc
- def f():
- print 'f() Do something'
学后的总是感觉就是:装饰器可以让函数轻装上阵,更重要的是将函数的约束放置于接口处,使意图更加明了,同时又不增加调用者的负担。
------------------------------------------------------------------------
在python中编程碰到过这样一件事情,需要给大量的函数做相同的操作,这样每个函数都去实现一遍这个功能显然是浪费时间。
#这是一个装饰器函数
def DecoratorFunc(func):
#Function就是对传入的func函数的包装,以便加入更多的功能
def Function():
printf('hello world')#简单的做一些额外操作,可以是其它操作
return func
return Function
现在有了装饰器函数,如何应用到其它函数上呢,用@来使用,如下
@DecoratorFunc
def run():
printf('my run function')
上面的函数就应用了DecoratorFunc这个装饰器的方法,则当我们调用run这个函数的时候,程序的运行是这样的,它先把run这个函数传入DecoratorFunc这个函数里面,然后对其进行简单的工作后返回结果,如果是上面的写法的话,最后输出应该是
hello world
my run function
这两句
这里只是简单的例子,装饰器函数可以做的很丰富
如果像上面的run函数有参数的话,则可以用下面的包装函数,多了参数而已
def DecoratorFunc(func):
#*args表示元祖参数,**kargs表示字典参数
def Function(*args,**kargs):
printf('hello world')
return func(*args,**kargs)
return Function
今天四群的群友“KindOf(5771067)”问到了函数修饰符,我本来也不甚了解,在请教Mr.Zhang后,将心得附录与此。
首先,什么是函数修饰符?函数修饰符就是对原有函数做一层包装。比如有以下两个函数:
- def func1():
print 'I am function func1'
def func2():
print 'I am function func2'
现在我们想为这两个函数增加一段共同的操作,这时就可以使用函数修饰符。首先我们定义一个修饰函数,先不做任何处理,仅仅打印一条横线:
- def de( f )
def _call_():
print '-------------------------------'
return f()
return _call_
使用这个修饰符:
- def de( f ):
def _call_():
print '-------------------------------'
return f()
return _call_
@de
def func1():
print 'I am function func1'
@de
def func2():
print 'I am function func2'
if __name__ == '__main__':
func1()
func2()
运行结果为:
- d:\我的文档\桌面>b.py
-------------------------------
I am function func1
-------------------------------
I am function func2
d:\我的文档\桌面>
可以看到,修饰符起作用了。
注意:
1.修饰符等价于包装调用:
- @de
def func1:
----- 等价于 ------
func1 = de( func1 )
2.修饰函数必须返回一个“可调用对象”:
- def de( f ):
def _call_():
return f()
return _call_ # 返回的是一个函数体,而非调用_call_()
下一个问题:如果各个被修饰函数有不同的参数,怎么处理呢?例如:
- def func1( lst1, lst2 ):
# 合并两个list,并打印各项
for item in lst1+lst2:
print item
def func2( dic ):
# 循环dict,打印各key-value
for k, v in dic.items():
print k, v
这两个函数参数不同,又都没有异常处理,现在我们想增加上,使用修饰符这样处理:
- import sys
import traceback
def de( f ):
def _call_( *args, **kwargs ):
try:
return f( *args , **kwargs )
except:
print 'param type error'
return _call_
@de
def func1( lst1, lst2 ):
for item in lst1+lst2:
print item
@de
def func2( dic ):
for k, v in dic.items():
print k, v
if __name__ == '__main__':
func1( [1,2], [3,4] )
func2( [1,2] )
这里使用了可变参数*args和**kwargs,这样会把所有接收来的参数,原样不动的再转给原函数,是惯用法。
以上程序运行结果:
- d:\我的文档\桌面>b.py
1
2
3
4
param type error
d:\我的文档\桌面>
阅读(2246) | 评论(0) | 转发(0) |