python装饰器

装饰器基础知识

装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
假如有一个名为decorate的装饰器:

@decorate
def target():
    print('running target()')

上述代码的效果与下述写法一样:

def target():
    print('running target()')

target = decorate(target)

两种写法的最终结果一样:上述两个代码片段执行完毕后得到的target不一定是原来那个target函数,而是decorate(target)返回的函数。
严格来说,装饰器只是语法糖。如前所示,装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。有时,这样做更方便,尤其是做元编程(在运行时改变程序的行为)时。
综上,装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二个特性是,装饰器在加载模块时立即执行。

python何时执行装饰器

装饰器的一个关键特性是,它们在被装饰的函数定义之后立即执行。

registry = []

def register(func):
    print('running register(%s)' % func)
    registry.append(func)
    return func

@register
def f1():
    print('running f1()')

@register
def f2():
    print('running f2()')

def f3():
    print('running f3()')

def main():
    print('running main()')
    print('registry ->', registry)
    f1()
    f2()
    f3()

if __name__ == "__main__":
    main()

程序运行的输出结果如下:

running register(<function f1 at 0x100631bf8>)
running register(<function f2 at 0x100631c80>)
running main()
registry -> [<function f1 at 0x100631bf8>, <function f2 at 0x100631c80>]
running f1()
running f2()
running f3()

结论
函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。这突出了python程序员所说的导入时和运行时之间的区别。

实现一个简单的装饰器

不带参数的装饰器

import time


def running_time(func):
    def wrapper():
        t0 = time.time()
        func()
        t1 = time.time()
        print('执行时间 ')
        print(t1 - t0)
    return wrapper


@running_time
def count_odds():
    count = 0

    for i in range(10000):
        if i % 2 == 0:
            count += 1
    print('奇数数量 ')
    print(count)


if __name__ == "__main__":
    count_odds()

带参数的装饰器

import time


def running_time(func):
    def wrapper(n):
        t0 = time.time()
        func(n)
        t1 = time.time()
        print('执行时间 ')
        print(t1 - t0)
    return wrapper


@running_time
def count_odds(n):
    print('正在计算0~{}的奇数数量'.format(n))
    count = 0

    for i in range(n):
        if i % 2 == 0:
            count += 1
    print('奇数数量 ')
    print(count)


if __name__ == "__main__":
    count_odds(20000)
上一篇:Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?错误


下一篇:用Callable创建线程