Python 标准库中设计精巧的装饰器

2017-10-15  本文已影响27人  bdd1b3ad7323

本文将介绍两个 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》>

上一篇下一篇

猜你喜欢

热点阅读