面试官:你了解装饰器吗?
XXX:了解
面试官:那你说说吧
XXX:装饰器是…
面试官:你先写一个装饰器,再写一个带参的装饰器
XXX:xxx。。。
面试官:你知道装饰器有哪些应用场景吗
XXX:xxx。。。(思考)
Python的装饰器(decorator)可以说是Python的一个神器,它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能。Python的装饰器同时也是Python学习从入门到精通过程中必需要熟练掌握的知识。今天尝试用浅显的语言解释下Python装饰器的工作原理及如何编写自己的装饰器吧。
应用场景
- 附加功能
- 数据的清理或添加:
- 函数参数类型验证 @require_ints 类似请求前拦截
- 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改
- 为函数提供额外的数据 mock.patch
- 函数注册
- 在任务中心注册一个任务
- 注册一个带信号处理器的函数
你可能还是不理解装饰器是啥?没有关系,下面我们详细的解析一下到底如何去实现装饰器。
简单的装饰器
import time
def getTime(func):
def inner():
start = time.time()
func()
end = time.time()
print('用时:{}秒'.format(end-start))
return inner
@getTime
def func1():
time.sleep(2)
print("Func1 is running.")
if __name__ == '__main__':
func1()
上面的代码,func1函数被getTime函数装饰,这个装饰器实现的功能是统计func1函数执行的时间,这其实就是一个最简单的装饰器。
如何你还是不太理解,我们换种写法
import time
def getTime(func):
def inner():
start = time.time()
func()
end = time.time()
print('用时:{}秒'.format(end-start))
return inner
def func1():
time.sleep(2)
print("Func1 is running.")
res = getTime(func1)
print(res)
本质上的装饰器
Python的装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数。这样我们可以在不改变被装饰函数的代码的情况下给被装饰函数或程序添加新的功能。
Python的装饰器广泛应用于引入日志,执行函数前预备处理,执行函数后清理功能,权限校验(如django中的@login_required和@permission_required装饰器),缓存,事务处理、性能测试(比如统计一段程序的运行时间)和插入日志等应用场景。有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码,增加一个函数的重用性。
试想你写了很多程序,一直运行也没啥问题。有一天老板突然让你统计每个程序都运行了多长时间并比较下运行效率。此时如果你去手动修改每个程序的代码一定会让你抓狂,而且还破坏了那些程序的重用性。聪明的程序员是绝不能干这种蠢事的。此时你可以编写一个@time_it的装饰器(代码如下所示)。如果你想打印出某个函数或程序运行时间,只需在函数前面@一下,是不是很帅?
由于Python装饰器的工作原理主要依赖于嵌套函数和闭包,所以我们必须先对嵌套函数和闭包有深入的了解。嵌套函数和闭包几乎是Python工作面试必考题哦。闭包相关的讲解请点击专题四:python之作用域和闭包详解
嵌套函数
如果在一个函数的内部还定义了另一个函数(注意: 是定义,不是引用!),这个函数就叫嵌套函数。外部的我们叫它外函数,内部的我们叫他内函数。
我们先来看一个最简单的嵌套函数的例子。我们在outer函数里又定义了一个inner函数,并调用了它。你注意到了吗? 内函数在自己作用域内查找局部变量失败后,会进一步向上一层作用域里查找。
def