larken

勤奋的人生才有价值

导航

第7章 函数装饰器和闭包

# 《流畅的Python》读书笔记
# 第7章 函数装饰器和闭包
# 函数装饰器用于在源码中“标记”函数,以某种方式增强函数的行为。这是一项强大的功能,但是若想掌握,必须理解闭包。
# nonlocal 是新近出现的保留关键字,在 Python 3.0 中引入。
# 除了在装饰器中有用处之外,闭包还是回调式异步编程和函数式编程风格的基础。
# 本章的最终目标是解释清楚函数装饰器的工作原理,包括最简单的注册装饰器和较复杂的参数化装饰器。但是,在实现这一目标之前,我们要讨论下述话题:
    #Python 如何计算装饰器句法
    #Python 如何判断变量是不是局部的
    #闭包存在的原因和工作原理
    #nonlocal 能解决什么问题
# 掌握这些基础知识后,我们可以进一步探讨装饰器:
    #实现行为良好的装饰器
    #标准库中有用的装饰器
    #实现一个参数化装饰器
    
# 7.1 装饰器基础知识
# 装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。 
# 装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。

# 示例 7-1 装饰器通常把函数替换成另一个函数
def deco(func):
    def inner():
        print('running inner()')
    return inner
@deco
def target():
    print('running target()')
print(target())
print(target)

# 7.2 Python何时执行装饰器
#装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。

# 示例 7-2 registration.py 模块
registry=[]
def register(func):
    print('running register(%s)'%func)
    registry.append(func)
    return func
@register
def f1():
    print('running f1()')
@register
def f2():
    print('running f2()')
def f3():
    print('running f3()')
def main():
    print('running main()')
    print('registry ->',registry)
    f1()
    f2()
    f3()
if __name__=='__main__':
    main()
# running register(<function f1 at 0x016F6540>)
# running register(<function f2 at 0x016F64F8>)
# running main()
# registry -> [<function f1 at 0x016F6540>, <function f2 at 0x016F64F8>]
# running f1()
# running f2()
# running f3()

# 7.3 使用装饰器改进“策略”模式
# 使用注册装饰器可以改进 6.1 节中的电商促销折扣示例。

# 示例 7-3 promos 列表中的值使用 promotion 装饰器填充

# 7.4 变量作用域规则
# 在示例 7-4 中,我们定义并测试了一个函数,它读取两个变量的值:一个是局部变量 a,是函数的参数;另一个是变量 b,这个函数没有定义它。

# 示例 7-4 一个函数,读取一个局部变量和一个全局变量

# 示例 7-5 b 是局部变量,因为在函数的定义体中给它赋值了

# 示例 7-6 反汇编示例 7-4 中的 f1 函数

# 示例 7-7 反汇编示例 7-5 中的 f2 函数

# 7.5 闭包
# 在博客圈,人们有时会把闭包和匿名函数弄混。
# 其实,闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。函数是不是匿名的没有关系,关键是它能访问定义体之外定义的非全局变量。
# 这个概念难以掌握,最好通过示例理解。

# 示例 7-8 average_oo.py:计算移动平均值的类

# 示例 7-9 average.py:计算移动平均值的高阶函数

# 示例 7-10 测试示例 7-9

# 示例 7-11 审查 make_averager(见示例 7-9)创建的函数

# 示例 7-12 接续示例 7-11

# 7.6 nonlocal声明

# 示例 7-13 计算移动平均值的高阶函数,不保存所有历史值,但有缺陷

# 示例 7-14 计算移动平均值,不保存所有历史(使用 nonlocal 修正)

# 7.7 实现一个简单的装饰器

# 示例 7-15 一个简单的装饰器,输出函数的运行时间

# 示例 7-16 使用 clock 装饰器

# 示例 7-17 改进后的 clock 装饰器

# 7.8 标准库中的装饰器
# Python 内置了三个用于装饰方法的函数:property、classmethod 和staticmethod。
# 另一个常见的装饰器是 functools.wraps,它的作用是协助构建行为良好的装饰器。

# 7.8.1 使用functools.lru_cache做备忘
# unctools.lru_cache 是非常实用的装饰器,它实现了备忘(memoization)功能。
# 这是一项优化技术,它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。
# LRU 三个字母是“Least Recently Used”的缩写,表明缓存不会无限制增长,一段时间不用的缓存条目会被扔掉。

# 示例 7-18 生成第 n 个斐波纳契数,递归方式非常耗时

# 示例 7-19 使用缓存实现,速度更快

# 7.8.2 单分派泛函数

# 示例 7-20 生成 HTML 的 htmlize 函数,调整了几种对象的输出

# 示例 7-21 singledispatch 创建一个自定义的htmlize.register 装饰器,把多个函数绑在一起组成一个泛函数

# 7.9 叠放装饰器

# 7.10 参数化装饰器

# 示例 7-22 示例 7-2 中 registration.py 模块的删减版,这里再次给出是为了便于讲解

# 7.10.1 一个参数化的注册装饰器

# 示例 7-23 为了接受参数,新的 register 装饰器必须作为函数调用

# 示例 7-24 使用示例 7-23 中的 registration_param 模块

# 7.10.2 参数化clock装饰器

# 示例 7-25 clockdeco_param.py 模块:参数化 clock 装饰器

# 示例 7-26 clockdeco_param_demo1.py

# 示例 7-27 clockdeco_param_demo2.py

# 7.11 本章小结
# 本章介绍了很多基础知识,虽然学习之路崎岖不平,我还是尽可能让路途平坦顺畅。毕竟,我们已经进入元编程领域了。
# 开始,我们先编写了一个没有内部函数的 @register 装饰器;最后,我们实现了有两层嵌套函数的参数化装饰器 @clock()。

# 7.12 延伸阅读

 

posted on 2019-03-18 00:58  larken  阅读(215)  评论(0编辑  收藏  举报