装饰器基础知识
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
假如有一个名为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)