zoukankan      html  css  js  c++  java
  • python 装饰器(三):装饰器实例(一)

    示例 7-15 定义了一个装饰器,它会在每次调用被装饰的函数时计时,然后把经过的时间、传入的参数和调用的结果打印出来。
    示例 7-15 一个简单的装饰器,输出函数的运行时间

    import time
    
    def clock(func):
        def clocked(*args):  #
            t0 = time.perf_counter()
            result = func(*args)  #
            elapsed = time.perf_counter() - t0
            name = func.__name__
            arg_str = ', '.join(repr(arg) for arg in args)
            print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
            return result
        return clocked #

    ❶ 定义内部函数 clocked,它接受任意个定位参数。
    ❷ 这行代码可用,是因为 clocked 的闭包中包含自由变量 func。
    ❸ 返回内部函数,取代被装饰的函数。示例 7-16 演示了 clock 装饰器的用法。
    示例 7-16 使用 clock 装饰器

    # clockdeco_demo.py
    
    import time
    from clockdeco import clock
    
    @clock
    def snooze(seconds):
        time.sleep(seconds)
    
    @clock
    def factorial(n):
        return 1 if n < 2 else n*factorial(n-1)
    
    if __name__=='__main__':
        print('*' * 40, 'Calling snooze(.123)')
        snooze(.123)
        print('*' * 40, 'Calling factorial(6)')
        print('6! =', factorial(6))

    运行示例 7-16 得到的输出如下:

    $ python3 clockdeco_demo.py
    **************************************** Calling snooze(123)
    [0.12405610s] snooze(.123) -> None
    **************************************** Calling factorial(6)
    [0.00000191s] factorial(1) -> 1
    [0.00004911s] factorial(2) -> 2
    [0.00008488s] factorial(3) -> 6
    [0.00013208s] factorial(4) -> 24
    [0.00019193s] factorial(5) -> 120
    [0.00026107s] factorial(6) -> 720
    6! = 720

    工作原理
    记得吗,如下代码

    @clock
    def factorial(n):
        return 1 if n < 2 else n*factorial(n-1)

    其实等价于:

    def factorial(n):
        return 1 if n < 2 else n*factorial(n-1)
    
    factorial = clock(factorial)

    因此,在两个示例中,factorial 会作为 func 参数传给 clock(参见示例 7-15)。然后, clock 函数会返回 clocked 函数,Python 解释器在背后会把 clocked 赋值给 factorial。
    其实,导入clockdeco_demo 模块后查看 factorial 的 __name__ 属性,会得到如下结果:

    >>> import clockdeco_demo
    >>> clockdeco_demo.factorial.__name__
    'clocked'
    >>>

    所以,现在 factorial 保存的是 clocked 函数的引用。自此之后,每次调用 factorial(n),执行的都是 clocked(n)。clocked 大致做了下面几件事。

    (1) 记录初始时间 t0。
    (2) 调用原来的 factorial 函数,保存结果。
    (3) 计算经过的时间。
    (4) 格式化收集的数据,然后打印出来。
    (5) 返回第 2 步保存的结果。

    这是装饰器的典型行为:把被装饰的函数替换成新函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外操作。

    示例 7-15 中实现的 clock 装饰器有几个缺点:不支持关键字参数,而且遮盖了被装饰函数的 __name__ 和 __doc__ 属性。示例 7-17 使用

    functools.wraps 装饰器把相关的属性从 func 复制到 clocked 中。此外,这个新版还能正确处理关键字参数。
    示例 7-17 改进后的 clock 装饰器

    # clockdeco2.py
    
    import time
    import functools
    
    def clock(func):
        @functools.wraps(func)
        def clocked(*args, **kwargs):
            t0 = time.time()
            result = func(*args, **kwargs)
            elapsed = time.time() - t0
            name = func.__name__
            arg_lst = []
            if args:
                arg_lst.append(', '.join(repr(arg) for arg in args))
            if kwargs:
                pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
                arg_lst.append(', '.join(pairs))
            arg_str = ', '.join(arg_lst)
            print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
            return result
        return clocked

    functools.wraps 只是标准库中拿来即用的装饰器之一。下一节将介绍 functools 模块中最让人印象深刻的两个装饰器:lru_cache 和singledispatch。








  • 相关阅读:
    POJ 3140 Contestants Division (树dp)
    POJ 3107 Godfather (树重心)
    POJ 1655 Balancing Act (树的重心)
    HDU 3534 Tree (经典树形dp)
    HDU 1561 The more, The Better (树形dp)
    HDU 1011 Starship Troopers (树dp)
    Light oj 1085
    Light oj 1013
    Light oj 1134
    FZU 2224 An exciting GCD problem(GCD种类预处理+树状数组维护)同hdu5869
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12944498.html
Copyright © 2011-2022 走看看