装饰器(Decorators)
装饰器是 Python 的一个重要部分。它是修改其他函数的功能的函数,有助于让我们的代码更简短
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
概括的讲,装饰器的作用就是为已经存在的函数或对象添加额外的功能。
为什么需要装饰器
我们假设你的程序实现了func_enter()和func_quit()两个函数。
def func_enter():
print "enter!"
def func_quit():
print "enter!" # bug here
if __name__ == '__main__':
func_enter()
func_quit()
运行结果:
enter!
enter!
(wda_python) bash-3.2$
但是在实际调用中, 我们发现程序出错了, 上面打印了2个enter。经过调试我们发现是func_quit()出错了
现在假如要求调用每个方法前都要记录进入函数的名称, 比如这样:
[DEBUG]: enter func_enter()
enter!
[DEBUG]: enter func_quit()
enter!
一种最直白简单的方式是这样写:
def func_enter():
print "[DEBUG]: enter func_enter()"
print "enter!"
def func_quit():
print "[DEBUG]: enter func_quit()"
print "enter!" # bug here
if __name__ == '__main__':
func_enter()
func_quit()
但是很low对吧, 我们可以试着这样写:
def debug():
import inspect
caller_name = inspect.stack()[1][3]
print '[BEBUG]: enter {}()'.format(caller_name)
def func_enter():
debug()
print "enter!"
def func_quit():
debug()
print "enter!" # bug here
if __name__ == '__main__':
func_enter()
func_quit()
看起来会好一点, 但是每个函数都要调用一次debug()函数,还是不太够, 万一如果又改需求进出不打印调用者了, 其他地方或者函数在打印, 又要大改
怎么办呢? 这个时候装饰器就可以派上用场了
怎么写一个装饰器
我们来看一个例子
按 Ctrl+C
def debug(func):
def wrapper():
print '[DEBUG]: enter {}()'.format(func.__name__)
return func()
return wrapper
@debug
def func_enter():
print "enter!"
@debug
def func_quit():
print "enter!" # bug here
if __name__ == '__main__':
func_enter()
func_quit()
按 Ctrl+C
运行结果:
[DEBUG]: enter func_enter()
enter!
[DEBUG]: enter func_quit()
enter!
(wda_python) bash-3.2$
这是一个最简单的装饰器, 但是有个问题, 如果被装饰的函数需要传入参数, 那么这个装饰器就坏了,因为返回的函数并不能接受参数
这里可以指定装饰器函数wrapper接受和原函数一样的参数, 比如:
#coding: utf-8
def debug(func):
def wrapper(something): # 这里指定一样的参数
print '[DEBUG]: enter {}()'.format(func.__name__)
return func(something)
return wrapper # 返回包装过的函数
@debug
def func_enter(something):
print "enter {}!".format(something)
@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!
这样解决了传参数的问题, 但是这里有个很大的问题是这里只适配了我们的func_enter和func_quit函数的参数, 如果要用来去装饰其他带参数的函数呢?
还好python提供可变参数*args和关键字参数**kwargs, 有这两个参数装饰器就可以用于任意目标函数了
#coding: utf-8
def debug(func):
def wrapper(*args, **kwargs): # 这里指定一样的参数
print '[DEBUG]: enter {}()'.format(func.__name__)
return func(*args, **kwargs)
return wrapper # 返回包装过的函数
@debug
def func_enter(something):
print "enter {}!".format(something)
@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$
带参数的装饰器
如果前面我们的装饰器需要完成的功能不仅仅是能在进入某个函数后打印出调用信息,还要指定log级别, 那么装饰器可以是这样:
#coding: utf-8