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

posted @ 2013-09-26 14:58  小郭学路  阅读(271)  评论(0编辑  收藏  举报