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。








  • 相关阅读:
    常用网站
    我的第一个 python 爬虫脚本
    在文件夹下所有文件中查找字符串(linux/windows)
    Python 列表 insert() 方法
    mysql 替换 tab 键 ( )
    访问权限的修饰符
    eclipse 快捷键
    位运算
    hadoop 环境搭建
    Hadoop 快速入门
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12944498.html
Copyright © 2011-2022 走看看