简单来说,装饰器 decorator 就是实现了一个通用的功能,然后将它应用到需要使用这个功能的函数上,避免不同函数含有相同功能的代码。
装饰器,它接收一个函数作为唯一参数,返回一个函数(可以是原函数也可以不是)。
1 无参数的装饰器
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2008-08-08')
# <=> now = log(now)
>>> now()
call now():
2008-08-08
把 @log
放到 now()
函数的定义处相当于执行了语句 now = log(now)
。 now()
函数仍然存在,只是同名的 now 变量指向了新函数,于是调用 now()
将执行新函数,即在 log()
函数中返回的 wrapper()
函数,并接手旧函数的所有参数。
2 有参数的装饰器
如果装饰器本身还想传入其它参数,那就需要嵌套。
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2008-08-08')
# <=> now = log('execute')(now)
>>> now()
execute now():
2008-08-08
3 functools.wraps
>>> now.__name__
'wrapper'
注意它的名字被暗改了! 应该把旧函数的属性赋给新函数。
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
4 属性装饰器
属性装饰器的完整形式是 property(getter, setter, deleter, doc)
。
# 不使用属性装饰器
class Student(object):
def __init__(self, name):
self.name = name
self.__score = None
def get_score(self):
return self.__score
def set_score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
s = Student('CS')
s.get_score()
s.set_score(1000)
# 安全地使用属性装饰器
class Student(object):
def __init__(self, name):
self.name = name
self.__score = None
def __get_score(self):
return self.__score
def __set_score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
score = property(__get_score, __set_score)
s = Student('CS')
s.score = 1000
# 简单地使用属性装饰器
class Student(object):
def __init__(self, name):
self.name = name
self.__score = None
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
s = Student('CS', 100)
s.score = 1000
5 建议
除非理由足够(调试或日志),不建议使用装饰器。