def debug(level):
def wrapper(func):
def inner_wrapper(*args, **kwargs):
print '[{level}]: enter {func}()'.format(level=level,func=func.__name__)
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@debug(level='Debug')
def func_enter(something):
print "enter {}!".format(something)
@debug(level='Debug')
def func_quit(something):
print "enter {}!".format(something) # bug here
if __name__ == '__main__':
func_enter("enter_func")
func_quit("quit_func")
运行结果:
[Debug]: enter func_enter()
enter enter_func!
[Debug]: enter func_quit()
enter quit_func!
(wda_python) bash-3.2$
基于类实现的装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。
class Test():
def __call__(self, *args, **kwargs):
print 'call me!'
t = Test()
t()
运行结果:
call me!
(wda_python) bash-3.2$
像__call__这样前后都带下划线的方法在Python中被称为内置方法,有时候也被称为魔法方法。重载这些魔法方法一般会改变对象的内部行为。上面这个例子就让一个类对象拥有了被调用的行为。
回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文)。那么用类来实现也是也可以的。我们可以让类的构造函数__init__()接受一个函数,然后重载__call__()并返回一个函数,也可以达到装饰器函数的效果。
class Debug_info(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print "[DEBUG]: enter function {func}()".format(func=self.func.__name__)
return self.func(*args, **kwargs)
@Debug_info
def func_enter(something):
print 'enter {}!'.format(something)
if __name__ == '__main__':
func_enter("enter_func")
运行结果:
[DEBUG]: enter function func_enter()
enter enter_func!
(wda_python) bash-3.2$
带参数的类装饰器
如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。然后在重载__call__方法是就需要接受一个函数并返回一个函数。
#coding: utf-8
class Debug_info(object):
def __init__(self, level='INFO'):
self.level= level
def __call__(self, func): # 接受函数
def wrapper(*args, **kwargs):
print "[{level}]: enter function {func}()".format(level=self.level,func=func.__name__)
func(*args, **kwargs)
return wrapper
@Debug_info(level='INFO')
def func_enter(something):
print 'enter {}!'.format(something)
if __name__ == '__main__':
func_enter("enter_func")
运行结果:
[INFO]: enter function func_enter()
enter enter_func!
(wda_python) bash-3.2$
内置的装饰器
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:
s = Student()
s.score = 9999
这显然不合逻辑。为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数:
class Student(object):
def get_score(self):
return self._score