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