美文网首页
Python 标准库中设计精巧的装饰器

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

作者: bdd1b3ad7323 | 来源:发表于2017-10-15 14:15 被阅读27次

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

    相关文章

      网友评论

          本文标题:Python 标准库中设计精巧的装饰器

          本文链接:https://www.haomeiwen.com/subject/kyccuxtx.html