本文将介绍两个 Python 标准库中设计精巧的装饰器。
functools.lru_cache
functools.lru_cache
是一种缓存装饰器,它可以把耗时函数的运算结果储存起来,下次再调用时直接从缓存池中取出结果,从而省略了运算过程。
我们举一个例子,首先导包
from functools import lru_cache, wraps
然后定义一个计步器
def pedometer(func):
@wraps(func)
def pedo(*args, **kwargs):
name = func.__name__
print(f'Func: {name} Args: {args}')
return func(*args, **kwargs)
return pedo
该装饰器可以显示被调用函数的输入参数。
再定义一个斐波那契函数
@pedometer
def fibonacci(n):
return n if n < 2 else fibonacci(n - 2) + fibonacci(n - 1)
先运行一下看看效果
print(fibonacci(6))
Output:
Func: fibonacci Args: (6,)
Func: fibonacci Args: (4,)
Func: fibonacci Args: (2,)
Func: fibonacci Args: (0,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (3,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (2,)
Func: fibonacci Args: (0,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (5,)
Func: fibonacci Args: (3,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (2,)
Func: fibonacci Args: (0,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (4,)
Func: fibonacci Args: (2,)
Func: fibonacci Args: (0,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (3,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (2,)
Func: fibonacci Args: (0,)
Func: fibonacci Args: (1,)
8
可以看到,使用了该装饰器后,函数的调用过程被详细的打印了出来。通过观察我们发现,有一些参数相同的函数被调用了许多次,从而导致了重复计算。所以我们引入functools.lru_cache
@lru_cache()
@pedometer
def fibonacci(n):
return n if n < 2 else fibonacci(n - 2) + fibonacci(n - 1)
看一下结果
print(fibonacci(6))
Output:
Func: fibonacci Args: (6,)
Func: fibonacci Args: (4,)
Func: fibonacci Args: (2,)
Func: fibonacci Args: (0,)
Func: fibonacci Args: (1,)
Func: fibonacci Args: (3,)
Func: fibonacci Args: (5,)
8
可以看出,对于重复的参数并没有重新计算。
注:lru_cache的原型是functools.lru_cache(maxsize=128, typed=False)
单分派泛函数
通过单分配泛函数,可以在不改动源代码前提下实现函数重载
from functools import singledispatch
import numbers
@singledispatch
def typed(obj):
return 'Object'
@typed.register(str)
def _(text):
return 'String'
@typed.register(numbers.Integral)
def _(n):
return 'Integer'
@typed.register(tuple)
def _(seq):
return 'Tuple'
测试
print(typed(1))
print(typed("1"))
print(typed((1, )))
print(typed([1, 2]))
Output
Integer
String
Tuple
Object
我们不使用singledispatch
装饰器时,实现以上功能,需要
def typedd(obj):
if isinstance(obj, str):
return 'String'
if isinstance(obj, numbers.Integral):
return 'Integer'
if isinstance(obj, tuple):
return 'Tuple'
return 'Obj'
一旦我们需要增加新的功能,就需要修改typedd
函数,不符合开放-封闭原则。
<来自《流畅的Python》>
网友评论