1 引言
ASP站长网装饰器(Decorators)可能是Python中最难掌握的概念之一了,也是最具Pythonic特色的技巧,深入理解并应用装饰器,你会更加感慨——人生苦短,我用Python。
2 初步理解装饰器
2.1 什么是装饰器
在解释什么是装饰器之前,我们有必要回顾一下Python中的一些思想和概念。我们都知道,Python是一门面向对象的语言,Python基本思想就是一些皆对象,数据类型是对象、类是对象、类实例也是对象……对于接下来我们要说的装饰器而言,最重要的是,函数也是对象!
你没看错,函数也和数据类型等概念一样,都是对象,那么既然数据类型可以进行赋值操作,那么函数是不是也可以赋值呢?当然可以!
def do_something():
print('完成一些功能')
if __name__ == '__main__':
do = do_something
do()
输出:
完成一些功能
看,原本我们定义的函数名是do_something,但我们把函数赋值给do后,也可以通过do()调用函数。不仅如此,函数当做参数传递给其他函数:
def do_something():
print('正在完成功能')
def func(f):
f()
if __name__ == '__main__':
func(do_something)
输出:
完成一些功能
正是因为Python中函数既可以赋值给其他变量名,也可以当做参数参数进其他函数,所以,上面的代码没有任何问题。
当然,毕竟被称呼为函数,有别有变量之类的概念,所以它也有自己的特性,例如在函数内部,还可以定义函数,甚至作为返回值返回:
def func():
def inner_func():
print('我是內部函数')
return inner_func
if __name__ == '__main__':
fun1 = func()
fun1()
输出结果:
我是內部函数
我们来总结一下函数的这几个特性:
可以赋值给其他变量;
可以作为参数传递给其他函数;
可以在内部定义一个函数;
可以当做返回值返回。
不要疑惑我为什么要着重说明Python中函数的这几个特性,因为装饰器中正是这几个特性为基石。为什么这么说呢?从本质上来说,装饰器就是一个函数,它也具有我们上面说到的4个特性,而且充分利用了这4个特性。装饰器接受一个普通函数作为参数,并在内部定义了一个函数,在这个内部函数中实现了一些功能,并调用了传递进来的函数,最后将内部函数作为返回值返回。如果一个函数把这个步骤全走了一遍,我们就可以认为,这个函数是一个装饰器函数,或者说装饰器。
我们来动手写一个装饰器:
def func(f):
def inner_func():
print('{}函数开始运行……'.format(f.__name__))
f()
print('{}函数结束运行……'.format(f.__name__))
return inner_func
def do_something():
print('正在完成功能')
if __name__ == '__main__':
do_something = func(do_something)
do_something()
输出结果:
do_something函数开始运行……
正在完成功能
do_something函数结束运行……
在上面代码中,我们将do_something方法作为参数传递给func方法,在func方法内部我们定义了一个inner_func方法,并在这个方法中添加了函数开始执行和结束执行的提示,在func方法最后,我们将inner_func作为参数返回,更加值得一说的是,我们重新将func函数的返回值赋给了do_something,所以,最后一行我们再次调用的do_something方法已经不再是最初的do_something函数,而是func方法内定义的inner_func函数,所以最后执行do_something函数时,会有函数开始执行和结束执行的提示功能,而后面再调用do_something函数时,也都直接使用do_something()。
正如你所预料,func函数就是一个装饰器,捋一捋你会发现,func函数把我们上面说过的所有特性、步骤全实现了。
如果你在别处见过Python装饰器的使用,你可能会疑惑,我们实现的func装饰器跟你见过的装饰器不都一样,因为在实际应用中,装饰器大多是与“@”符号结合起来使用。其实“@”符号所实现的功能就是 do_something = func(do_something)这行代码的功能。来,我们尝试一下使用“@”:
def func(f):
def inner_func():
print('{}函数开始运行……'.format(f.__name__))
f()
print('{}函数结束运行……'.format(f.__name__))
return inner_func
@func
def do_something():
print('正在完成功能')
if __name__ == '__main__':
do_something()
输出结果:
do_something函数开始运行……
正在完成功能
do_something函数结束运行……
之前我们知道,func函数就是一个装饰器,所以使用“@”符号时,我们只需要在被装饰的函数前面加上“@func”就表示该函数被func装饰器装饰,在需要处直接调用do_something函数即可。