python基础学习(八)-- 装饰器
import time import functools # 函数对象有一个__name__属性 可以拿到函数的名字 def now(): return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), time.localtime() f = now print(now.__name__) print(f.__name__) # 但是现在我们要增强now()函数的功能 比如需要在函数调用前后打印日志 但又不想改变函数的定义 这种在代码运行期间动态添加功能的方式 称之为decorator(装饰器) # 本质上decorator就是一个可以返回函数的高阶函数。所以我们要定义一个可以打印日志的decorator的,可以定义如下: def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # 上面的log是一个decorator 所以接受一个函数作为参数 并返回一个函数。我们需要借助py的@语法,将decorator置于函数的定义处 @log def now_new(): return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) print(now_new()) # 在调用now_new函数前 会先打印一行日志 再调用now_new函数 相当于执行了now_nwe = log(now_new) # 由于log是一个decorator 返回一个函数 所以原来的now_new函数还在 只不过是现在的 now_new指向了一个新的函数 所以调用now_new将执行一个新函数 即log函数中返回的wrapper函数 # wrapper函数的参数定义是(*args, **kw)所以可以接收任意参数 在wrapper函数内部 先print输出 再调用now_new函数 # 如果decorator本身需要传入参数,那就需要编写一个返回decorator的函数,比如要自定义log的文本 print('-------------------') def new_log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__) + '--------\r\n') return func(*args, **kw) return wrapper return decorator @new_log('execute') def new_now(): return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) # print(new_now()) # 这个三层嵌套的效果相当于: new_now = new_log('execute')(new_now) print(new_now.__name__) # 前面都没有问题 但是最后的new_now.__name__变了 因为返回的wrapper函数的__name__就是wrapper,我们需要吧原始的__name__等属性复制到wrapper函数中 否则一些依赖函数签名的函数代码就会执行出错 # 不需要编写wrapper.__name__ = func.__name__这样的代码 python内置的 functools.wraps就可以搞定 写法如下 def log(func): # 这里的wraps我猜也算是一个装饰器吧 @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # practice:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间: def log_time(func): @functools.wraps(func) def wrapper(*args, **kw): start_time = time.time() res = func(*args, **kw) end_time = time.time() print('%s ran in %s seconds' % (func.__name__, (end_time - start_time) * 1000)) return res return wrapper @log_time def test(x, y): time.sleep(1) return x + y print(test(1, 2))