0X00 给一个方法计时
现在我们有一个需求,需要给程序中的一部分方法计时,以监控他们执行完具体用了多久。那么在没有装饰器的情况下我们会写出类似这样的代码:
1 2 3 4 5 6 7 8 9 10
| import datetime
def foo(): pass
start = datetime.datetime.now() foo() end = datetime.datetime.now() print((end - start).seconds)
|
0X01 引入装饰器
如果只是临时给一个方法使用也不是不行,但是如果我们需要监控大量的方法呢?众所周知Python中function
也是可以作为参数传递的,那来看一下下面这种写法呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import datetime
def timer(func): def run_func(): start = datetime.datetime.now() result = func() end = datetime.datetime.now() print((end - start).seconds) return result
return run_func
def foo(): pass
def bar(): pass
timer(foo)() timer(bar)()
|
因为上面timer(foo)返回的结果是一个func
,所以后面需要再加一对括号来调用这个方法
这种方法只写了一次计时的逻辑,但是可以给任意一个方法使用,其实这时候def timer(func)
已经是装饰器了,下面调用的方法timer(foo)/timer(bar)
也是正确的装饰器使用方法。那是不是觉得和常见的装饰器使用不太一样呢?其实常见的@timer
用法是Python中提供的一种语法糖。
0X02 @语法糖
其实上面的代码就可以直接使用@语法糖了,具体用法是这样:
1 2 3 4 5 6
| @timer def foo(): pass
foo()
|
语法糖实际只是便于我们编码的一种设计,按理说一切被称为语法糖的东西都不是必须的。比如说这里的语法糖完全可以用上面的方法来应用装饰器,但是为什么还是设计了这个语法糖呢?我们对比下面两种方法的调用,我们假设get_data_from_server/db
是一个耗时较久的方法:
1 2 3 4 5 6 7 8 9 10 11 12
| def get_data_from_server(): pass
@timer def get_data_from_db(): pass
timer(get_data_from_server))()
get_data_from_db()
|
这两种方法看起来明显是后面get_data_from_db
的用法更易读。所以这处语法糖的出现能大幅度的提升代码的可读性,更能提升维护性质:当代码中调用了1W次非语法糖形式的装饰器计时时,取消计时就要修改1W行代码;如果用了@语法糖,那就只需要将方法定义处的@timer
注释掉就可以了。
0X03 传参
“那要传参咋搞哇?” “这么搞”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
import time import datetime
def timer(func): def run_func(secs): start = datetime.datetime.now() result = func(secs) end = datetime.datetime.now() print((end - start).microseconds) return result
return run_func
@timer def foo(secs): time.sleep(secs)
@timer def bar(secs): time.sleep(secs)
if __name__ == '__main__': foo(3) bar(5)
|
“那我要是有好几个参数呢,怎么搞?” “就还是这么搞啊”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def timer(func): def run_func(*args): start = datetime.datetime.now() result = func(*args) end = datetime.datetime.now() print((end - start).microseconds) return result
return run_func
@timer def foo(start_secs, end_secs): time.sleep(random.randint(start_secs, end_secs))
foo(3, 5)
|