@装饰器这个名称可能更适合在编译器领域使用,因为它会遍历并 注解句法树。
@除了在装饰器中有用处之外,闭包还是回调式异步编程和函数式编程风格的基础。
7.1 装饰器基础知识
@装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。2 装饰器可能会处理被装 饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象
@装饰器可以像常规的可调用对象那样调用,其参 数是另一个函数。有时,这样做更方便,尤其是做元编程(在运行时改变程序的行为)时。
@综上,装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二个特性是,装饰器 在加载模块时立即执行。
7.2 Python何时执行装饰器
@装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时 (即 Python 加载模块时)
@主要想强调,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用 时运行。这突出了 Python 程序员所说的导入时和运行时之间的区别。
• 装饰器函数与被装饰的函数在同一个模块中定义。实际情况是,装饰器通常在一个模块 中定义,然后应用到其他模块中的函数上。
• register 装饰器返回的函数与通过参数传入的相同。实际上,大多数装饰器会在内部定 义一个函数,然后将其返回。
7.3 使用装饰器改进“策略”模式
@无
7.4 变量作用域规则
@python会将在函数定义体中赋值的变量假定为局部变量。只读情况下才会去全局查找。
@如果在函数中赋值时想让解释器把变量当成全局变量,要使用global声明;
7.5闭包
@闭包:函数是不是匿名的没有关系,关键是他能访问定义体之外定义的非全局变量。
@注意:嵌套函数的内部函数可以访问外部函数的变量。
@嵌套函数的闭包
此时自由变量的名字保存在返回的averager函数 的__CODE__属性:freevars 中。而自由变量的真正值保存在averager函数的__closure__属性->cell_contents中
@综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,虽然变量的作用域不可用了,但函数保存了自由变量的引用,可以使用真正的值。
@注意:只有嵌套在其他函数中的内部函数才可能处理不在全局作用域中的外部变量。
7.6 nonlocal声明
@c#的闭包把方法作为类来处理,而python则是将闭包数据关联到方法。
@为了解决闭包数据无法赋值的问题(编译器会把闭包数据当作局部变量),python3引入了nonlocal声明,它的作用是把变量标记为自由变量。
@为了再python2中对闭包数据赋值,你需要将变量存储为可变对象。
7.7 实现一个简单的装饰器
@装饰器的典型行为:把被装饰的函数替换成新函数,二者接收相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外操作。
@functools.Wraps 装饰器 用来把被装饰函数的name,doc 赋值到装饰器函数,避免装饰器函数覆盖掉被装饰函数。
7.8 标准库中的装饰器
@ python内置了三个装饰器:property,classmethod,staticmethod,还有一个常见的装饰器是functools.wraps
7.8.1 使用functools.lru_cache做备忘
@Lru_cache实现了备忘功能,这是一项优化计数,他把耗时的结果保存起来,避免传入相同的参数时重复计算。同时一段时间不用的缓存条目会被扔掉,主要场景在于优化递归算法,同时在web中获取信息的应用中也能发挥巨大作用
@lru_cache使用两个可选的参数来配置,它的签名是:
functools.lru_cache(maxsize=128, typed=False)
参数:maxsize 指定存储多少个调用的结果,存储满了之后,旧的结果会被扔掉。为了得到最佳性能,maxsize应该设为2的幂。
参数:typed 将不同类型参数得到的结果分开保存,即把认为相等的浮点数和整数参数区别开。
@被lru_cahe修饰的函数,它的所有参数必须都是可散列的。
7.8.2 单分派泛函数
@Python 3.4 新增的 functools.singledispatch 装饰器可以把整体方案拆分成多个模块,甚 至可以为你无法修改的类提供专门的函数。使用 @singledispatch 装饰的普通函数会变成 泛函数(generic function):根据第一个参数的类型,以不同方式执行相同操作的一组函 数。
@functools.singledispatch 是 Python 3.4 增 加 的,PyPI 中的singledispatch 包(https://pypi.python.org/pypi/singledispatch)可以向后兼容Python 2.6 到 Python 3.3。
@singledispatch 类似于函数重载,但有必要使用吗
@专门函数的名称无关紧要;_ 是个不错的选择,简单明了
@ 可以叠放多个 register 装饰器,让同一个函数支持不同类型。
@只要可能,注册的专门函数应该处理抽象基类(如 numbers.Integral 和 abc.MutableSequence), 不要处理具体实现(如 int 和 list)。这样,代码支持的兼容类型更广泛
7.9 叠放装饰器
@把 @d1 和 @d2 两个装饰器按顺序应用到 f 函数上,作用相当于 f = d1(d2(f))。
7.10 参数化装饰器
@解析源码中的装饰器时,Python 把被装饰的函数作为第一个参数传给装饰器函数。那怎么 让装饰器接受其他参数呢?答案是:创建一个装饰器工厂函数,把参数传给它,返回一个 装饰器,然后再把它应用到要装饰的函数上
@简单来说:
一般装饰器使用语法:@register1,
而参数化装饰器语法:@register2( )
看到差异了吗?函数register2( ) 的返回值函数才是装饰器,尽管它自身也是装饰器。
@参数化装饰器通常 会把被装饰的函数替换掉,而且结构上需要多一层嵌套。这会相当复杂
7.10.2 参数化clock装饰器
@多级参数化装饰器,无
7.11本章小结
@参数化装饰器基本上都涉及至少两层嵌套函数,如果想使用 @functools.wraps 生成装饰 器,为高级技术提供更好的支持,嵌套层级可能还会更深,比如前面简要介绍过的叠放装 饰器。
@ functools 模块提供的两个出色的函数装饰器:@lru_cache() 和 @singledispatch。
@若想真正理解装饰器,需要区分导入时和运行时,还要知道变量作用域、闭包和新增的 nonlocal 声明。掌握闭包和 nonlocal 不仅对构建装饰器有帮助,还能协助你在构建 GUI 程序时面向事件编程,或者使用回调处理异步 I/O。
@。如果想使用现代 的技术实现多分派泛函数,并支持在生产环境中使用,可以用 Martijn Faassen 开发的 Reg (http://reg.readthedocs.io/en/latest/)。 Martijn 还是模型驱动型REST 式 Web 框架Morepath (http://morepath.readthedocs.org/en/latest/)的开发者。
杂谈:装饰器模型尽管可以使用函数装饰器来实现,但一般来说,实现“装饰器”模式时最好使用类表示装饰器和 要包装的组件。