Chap 6 使用一等函数实现设计模式
6.1 “策略”模式
策略模式是定义一系列算法,将它们一一封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。
- 上下文 context
把一些计算委托给实现不同算法的可互换组件,它提供服务。 - 策略 strategy
实现不同算法的组件的共同接口 - 具体策略
“策略”的具体子类
在经典的“策略”模式中,具体策略都是一个类,它们没有状态,只有一个函数,可以用函数重构。
策略对象通常是很好的享元。
享元是可共享的对象,可以同时在多个上下文中使用。
共享是推荐的做法,这样不用在多个上下文中使用相同的策略时不断新建具体策略对象,减少消耗。
函数比用户定义的类轻量级,而且无需使用“享元”模式,因为各个策略函数字Python编译模块时只会创建一次。
6.2 “命令”模式
命令模式的目的是解耦调用操作的对象(调用者)和提供实现的对象(接收者)。
利用一等函数重构命令模式的方法与策略模式相似:将实现单方法接口的类的实例替换为可调用对象。
Chap 7 函数装饰器和闭包
7.1 装饰器基础知识
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后将它返回,或者将其替换成另一个函数或者可调用对象。
1 | def deco(func): |
此时的target()是对inner()的引用。
7.2 Python何时执行装饰器
装饰器在被装饰的函数定义之后立即运行,即导入时(Python加载模块时)。
函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。
装饰器常有以下用法:
- 装饰器通常在一个模块中定义,然后应用到其他模块的函数上
- 大多数装饰器会在内部定义一个函数,然后将其返回
7.4 变量作用域规则
Python不要求声明变量,但是假定在函数定义体中赋值的变量是局部变量。
在函数中复制时想让解释器将变量当成全局变量,使用global
声明。1
2
3
4
5
6b = 6
def f3(a):
global b
print(a)
print(b)
b = 9
7.5 闭包
闭包指延伸了作用域的函数,其中包含定义体内引用、但是不在定义体中定义的非全局变量。1
2
3
4
5
6
7def make_average():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total / len(series)
return averager
series
是自由变量(指未在本地作用域中绑定的变量)
series
的绑定在返回avg
函数的__closure__
属性中,avg.__closure__
中的各个元素对应于avg.__code__.co_freevars
中的一个名称。这些元素是cell对象,有个cell_contents
属性,保存着真正的值。
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
7.6 nonlocal声明
如果将7.5中的程序修改为只保存总值和个数1
2
3
4
5
6
7
8def make_average():
count = 0
total = 0
def averager(new_value):
count += 1
total += new_value
return total / count
return averager
这样的代码是,count+=1
的操作相当于对count赋值,把count当作局部变量,会出现未声明先引用的问题。在7.5的代码中,列表是可变的对象使得这个问题不会出现,但是数字、字符串、元组等是不可变类型,只能读取,不能更新。
为了解决这个问题,Python 3 引入了nonlocal
声明,将变量标记为自由变量1
2
3
4
5
6
7
8
9def make_average():
count = 0
total = 0
def averager(new_value):
nonlocal count, total
count += 1
total += new_value
return total / count
return averager
7.8 标准库中的装饰器
functools.wraps
协助构建行为良好的装饰器
functools.lru_cache
实现了备忘功能,是一项优化技术,把耗时的函数的结果保存起来,避免传入相同的参数时重复计算(eg: 斐波那契数递归函数)
LRU是Least Recently Used的缩写1
2
3
4
5
6
7
8
9
10import functools
from clockdeco import clock
def fibonacci(n):
if n < 2 :
return n
return fibonacci(n-2) + fibonacci(n-2)
if __name__ == '__main__':
print(fibonacci(6))
单分派泛函数
functools.singledispatch
装饰器将整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。
使用@singledispatch
装饰的普通函数会变成泛函数:根据第一个参数的类型,以不同方式执行相同操作的一组函数,
7.9 叠放装饰器
把@d1
和@d2
两个装饰器按顺序应用到f
函数上,作用相当于f = d2(d2(f))
7.10 参数化装饰器
Python会将被装饰函数作为第一个参数传给装饰器,要传入其他参数,需要创建一个装饰器工厂函数,把参数传给他,返回一个装饰器,然后再把它应用到要装饰的函数上。
1 | registry = [] |
如果要想常规函数使用register
,将f
添加到registry
中,需要使用register()(f)
或register(active=False)(f)
(删除/不想添加)
参数化装饰器通常会把被装饰的函数替换掉,而且结构上需要多一层嵌套