装饰器

简单来说,装饰器 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 建议

除非理由足够(调试或日志),不建议使用装饰器。