Python:装饰器

【认识装饰器】

按我的理解,装饰器就是修饰函数或者类的函数,即用函数在被装饰函数在调用前后、被装饰类在创建之前进行额外的操作。

(本文例子参考书籍:【Python.in.Practice(2013.8)].Mark.Summerfield】)

【使用方法】

在类或者函数定义前加上一行@+装饰器,类似语法糖

【函数装饰器】

1、作用:在被装饰函数调用前后,进行额外操作,如加上运行日志、时间戳、修改函数返回值等等,但不能修改被装饰函数本身内容

2、参数:被装饰函数是其唯一参数

3、缺点:被装饰函数__name__/__doc__属性被修改成装饰器wrapper函数的__name__/__doc__

                   这会影响错误信息跟踪,不利于程序调试。可使用functools.wraps装饰器消除影响

4、整体模板:

     (1)创建装饰函数wrapper(可带参数);

     (2)在wrapper里调用被装饰函数。在调用前后,进行额外操作, 如参数类型转换等。

                返回值方面,可以直接返回被装饰函数的结果retValue,或者返回其他结果,如修改retValue的值等;

     (3)decorator返回wrapper函数;

     (4)被装饰函数是decorator的唯一参数。

5、例子:请留意函数的返回值、函数的__doc__、__name__属性

def funcdecorator(func):
    def wrapper():
        '''wrapper函数
        '''
        print '[%s] %s() called' %(ctime(),func.__name__)
        func()
        return 1
        #return func()
    return wrapper

@funcdecorator
def foo():
    print 'foo'
    return 2
if __name__ == '__main__':
    print foo()
    sleep(2)
    print foo()
    sleep(3)
    print foo()
    print foo.__name__
    print foo.__doc__

输出结果如下:

[Tue Dec 09 10:05:57 2014] foo() called
foo
1
[Tue Dec 09 10:05:59 2014] foo() called
foo
1
[Tue Dec 09 10:06:02 2014] foo() called
foo
1
wrapper
wrapper函数

【装饰器函数】

1、作用:装饰器函数,即(函数)装饰器的函数,用来参数化装饰器

2、参数:任意参数,但要在@的时候添加参数

3、整体模板:

     (1)创建装饰器decorator;

     (2)同装饰器模板;

     (3)返回装饰器decorator。

4、列子:你会发现parameterize_decorator(return_type=float)整个就是装饰器,

请留意,使用@functools.wraps(func)后的不同

def parameterize_decorator(return_type=float):
    def plain_args_and_return(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            '''wrapper
            '''
            args = [float(arg) for arg in args]
            kwvalues = [float(kwargs[x]) for x in kwargs.keys()]
            args = args + kwvalues
            return return_type(func(*args))
        return wrapper
    return plain_args_and_return

@parameterize_decorator(return_type=int)
def average(first,second,*rest):
    numbers = (first,second) + rest
    return sum(numbers)/len(numbers)
        
if __name__ == '__main__': 
    print average(1,2,'11',9,'10',var=9)
    print average.__name__
    print average.__doc__
结果如下:

7
average
None

【类装饰器】

1、作用:对类进行额外的操作,这个没必要深究

2、注意:以下类的装饰器,被装饰类都需要继承object或者object子类

3、列子:

def classdecorator(cls):
    print 'INFO begin initialize'
    return cls
@classdecorator
class student(object):
    '''name,sid,sex,phonenum,age等属性可修改,可获取
    sid: 年后面加上五位数字
    sex: female male
    phonenum: 11为数字
    age: 整型,合理区间[10,100]
    '''
    def __init__(self,name,sid):
        self._name = name
        self._sid = sid

if __name__ == '__main__': 
    st = student(u'李三','123456')

【类装饰器函数】

1、作用:参数化类装饰器,可用于添加属性、属性值检查、替代子类等

2、列子:

def classdecoratorfunc(name,validate,doc=None):
    def classdecorator(cls):
        '''cls mean class
        '''
        private_name = '_' + name
        def getter(self):
            return getattr(self,private_name)
        def setter(self,value):
            validate(value)
            setattr(self,private_name,value)
        setattr(cls,name,property(getter,setter,doc=doc)) #关键点
        return cls
    return classdecorator
#属性检查 validator
def is_student_id(value):
    valid = True
    try:
        dt = datetime(int(value[0:4]),1,1)
    except:
        valid = False
    rest = str(value[4:])
    if len(rest) != 5 or not rest.isdigit():
        valid = False
    if not valid:
        raise ValueError('student id must like year+ five digits')
    
def is_valid_sex(value):
    if not isinstance(value,(str,unicode)):
        raise ValueError('student sex variable should be string or unicode') 
    if value.lower() not in ['female','male']:
        raise ValueError('student sex should be female or male,please ignore the third one') 

def is_valid_phonenum(value):
    if not isinstance(value,(str,unicode)):
        raise ValueError('student phonenum variable should be 11 digits string') 
    if not value.isdigit() or len(value) != 11:
        raise ValueError('student phonenum variable should be 11 digits string') 
    
def is_valid_age(value):
    try:
        age = int(value)
    except:
        raise ValueError('student age would be between 10 and 100')
    if age not in xrange(10,101):
        raise ValueError('student age would be between 10 and 100')

@classdecoratorfunc('sid',is_student_id)
@classdecoratorfunc('sex',is_valid_sex)
@classdecoratorfunc('phonenum',is_valid_phonenum)
@classdecoratorfunc('age',is_valid_age)
class student(object):
    '''name,sid,sex,phonenum,age等属性可修改,可获取
    sid: 年后面加上五位数字
    sex: female male
    phonenum: 11为数字
    age: 整型,合理区间[10,100]
    '''
    def __init__(self,name):
        self._name = name

if __name__ == '__main__': 
    st = student(u'李三')
    st.sid = '201400001'
    print st.sid
    print 'class: ' + str(st.__class__)
    print 'class: ' + str(st.__doc__)
    print 'class: ' + str(st.__str__())
    st.sid = '00010s001'
    print st.sid 

结果:

201400001
class: <class '__main__.student'>
class: name,sid,sex,phonenum,age等属性可修改,可获取
    sid: 年后面加上五位数字
    sex: female male
    phonenum: 11为数字
    age: 整型,合理区间[10,100]
    
class: <__main__.student object at 0x0173E770>
    st.sid = '00010s001'
  File "D:\personalstation\source\Demo\Decorator_Demo.py", line 39, in setter
    validate(value)
  File "D:\personalstation\source\Demo\Decorator_Demo.py", line 56, in is_student_id
    raise ValueError('student id must like year+ five digits')
ValueError: student id must like year+ five digits

【装饰器函数与线程锁】

ActiveState有一篇关于装饰器函数与同步锁的经典例子:http://code.activestate.com/recipes/577105-synchronization-decorator-for-class-methods/

要看懂这篇代码,需要明白实例成员与类成员的区别,先看个段代码:

>>> class A:
	foo = [] #类成员

>>> class B:
	def __init__(self):
		self.foo = [] #实例成员

>>> a1,a2 = A(),A()
>>> b1,b2 = B(),B()
>>> a1.foo.append(6)
>>> b1.foo.append(9)
>>> a2.foo
[6]
>>> b2.foo
[]
>>> 

对比a2.foo与b2.foo的值,实例成员与类成员的区别,就一目了然,具体如下:

1、实例成员为单个实例所有;

2、类成员为整个类所有,即被所有类实例化对象所有。

综上,个人建议,线程锁最好作为类成员,但还是具体问题具体分析。

在多线程编程中,使用装饰器函数可以我们节省很多重复代码,例子请看ActiveState。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值