装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
假如有一个名为decorate的装饰器:
@decorate def target(): print('running target()')
上述代码的效果与下述写法一样:
def target(): print('running target()') target = decorate(target)
两种写法的最终结果一样:上述两个代码片段执行完毕后得到的target不一定是原来那个target函数,而是decorate(target)返回的函数。
严格来说,装饰器只是语法糖。如前所示,装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。有时,这样做更方便,尤其是做元编程(在运行时改变程序的行为)时。
综上,装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二个特性是,装饰器在加载模块时立即执行。
装饰器的一个关键特性是,它们在被装饰的函数定义之后立即执行。
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)