Python之装饰器
该文介绍的比较好:
http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
http://www.codecho.com/understanding-python-decorators/
http://blog.csdn.net/thy38/article/details/4471421
装饰器通过返回包装对象实现间接调用,以此来插入额外逻辑。
>>> @check_args ... def test(*args): ... print args
还原成容易理解的方式:
>>> test = check_args(test)
另一种方式:
#!/usr/bin/env python26 #-*- coding:utf-8 -*- def check_args(func): def wrap(*args): args = filter(bool,args) func(*args) return wrap #返回wrap函数对象 @check_args def test(*args): print args print type(test) #通过wrap(test(args))完成调用 test(1,0,2,"",[],3)
过程:
1、将目标函数对象test作为参数传递给装饰器check_args
2、装饰器返回包装函数wrap实现对test的间接调用
3、原函数名字test被重新关联到wrap,所有对该名字的调用实际都是调用wrap
装饰器不一定非得是个函数返回包装对象,也可以是个类,通过__call__完成目标调用
#!/usr/bin/env python26 #-*- coding: utf-8 -*- class CheckArgs(object): def __init__(self,func): self._func = func def __call__(self,*args): args = filter(bool,args) self._func(*args) #生成CheckArgs实例 @CheckArgs def test(*args): print args #名字指向该实例 print type(test) #每次都是通过该实例的__call__调用 test(1,0,2,"",[],3)
用类装饰器对象实例替代原函数,以后的每次调用的都是该实例的__call__方法。这种写法啰嗦,还得注意避免在装饰器对象上保留状态。
Class
为Class提供装饰器同样简单,无非是将类型对象做为参数而已
参数
参数让装饰器拥有变化,也更加灵活。只需要两步才能完成:
先传参数,后送类型
#!/usr/bin/env python26 #-*- coding: utf-8 -*- def table(name): def _table(cls): cls.__table__ = name return cls return _table @table("t_user") class User(object): pass @table("t_blog") class Blog(object): pass print User.__table__ print Blog.__table__
只比无参数版本多了最外层传递参数的调用,其他完全相同
User = table("t_user")(User)
嵌套
可以在同一个目标上使用多个装饰器
#!/usr/bin/env python26 #-*- coding: utf-8 -*- def A(func): print "A" return func def B(func): print "B" return func @A @B def test(): print "test" test()
等同于:
test = A(B(test))
functools.wraps
如果装饰器返回的是包装对象,那么有些东西必然是不同的。
#!/usr/bin/env python26 #-*- coding: utf-8 -*- def check_args(func): def wrap(*args): return func(*filter(bool,args)) return wrap @check_args def test(*args): """test function""" print args print test.__name__
#输出为wrap,而不是test
print test.__doc__
#输出为None,没有test对象的说明
原因在于test是wrap
test的调用者检查某些特殊属性,这个wrap就会暴露。
可以使用functools.wraps解决这个问题。
#!/usr/bin/env python26 #-*- coding: utf-8 -*- import functools def check_args(func): @functools.wraps(func) def wrap(*args): return func(*filter(bool,args)) return wrap @check_args def test(*args): """test function""" print args print test.__name__ print test.__doc__
functools.wraps是装饰器的装饰器,它的作用是将原函数对象的指定属性复制给包装函数对象,默认有__module__、__name__、__doc__或者通过参数选择
装饰器都能干嘛?
*AOP: 身份验证、参数检查、异常日志等等
*Proxy:对目标函数注入权限管理
*Context:提供函数级别的上下文环境,比如Synchronized(func)同步
*Caching:先检查缓存是否过期,然后再决定是否调用目标函数
*metaprogramming