在 Web 框架 Flask 中,最常看到的或许是以@app.route
开头的那行代码。由于还是刚接触 Flask,所以对这种语法还不熟悉。看了这一章,发现原来是装饰器,又一新知识。
什么是装饰器?它是一个可调用对象,接受一个函数并返回一个函数或可调用对象。期间,装饰器会做一些额外的工作。
书上介绍了装饰器有两大特性,一个是「能把被装饰的函数替换成其他函数」,另一个是「装饰器在加载模块时立即执行」。常用的方式是把装饰器定义在一个模块中,返回的是内部新定义的函数。此外,需要注意的是导入时和运行时的区别。
之后的闭包,保证了内部函数代码的正确运行。什么是闭包?
闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。
闭包依旧是函数,只是其中包含了即非全部、也非本地变量的变量。这样的变量叫做自由变量(free variable)。当要改变自由变量时,自由变量会变成本地变量。关键字nonlocal
可以保护自由变量。
书中介绍了functools
模块中的三个装饰器。第一个是wraps
,它能把相关属性从被装饰的函数中正确地复制到装饰器的内部函数中。
另一个是lru_cache
,这个装饰器把函数结果保存了起来,避免传入相同参数时重复计算。使用它跟之前的例子有点不同:@functools.lru_cache()
,因为它可以接受配置参数,即缓存结果的数目和是否把不同类型的结果分开保存。被lru_cache
装饰的函数的所有参数必须是可散列的,因为它使用字典存储结果,键根据调用时传入的定位参数和关键字参数创建。
最后一个是singledispatch
,被它装饰的函数会变成泛函数(generic function),类似 Lisp 中的 generic function。在 Java 的类中,类似于方法重载。
装饰器一个强大的能力就是支持叠放,另一个是参数化。叠放不奇怪,装饰器返回的就是函数或可调用对象。而参数化中,接受参数的装饰器叫做装饰器工厂函数,它返回的是一个装饰器,之后把它应用到要装饰的函数上。