【认识装饰器】
按我的理解,装饰器就是修饰函数或者类的函数,即用函数在被装饰函数在调用前后、被装饰类在创建之前进行额外的操作。
(本文例子参考书籍:【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。