Python装饰器之函数运行时间

If you put your mind to it, you can accomplish anything. ——《回到未来》

有志者,事竟成。

目标

  在《requests中文乱码问题》这篇文章中提到了两种解决方式,这两种方式究竟哪种方式是值得推荐的那?一般来说抓取网页与持久化储存是最耗时的,要想提高抓取效率就得从这两方面入手。那我们就从时间入手,在不改变原代码的情况下,可以使用装饰器来运算两种方式运行所需要的时间。

代码实现

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import time
import requests
from functools import wraps


# 运行时间装饰器
def run_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_ts = time.time()
func(*args, **kwargs)
end_ts = time.time()
second = end_ts - start_ts
return second
return wrapper


@run_time
# 手动指定编码
def getHtml(url):
response = requests.get(url)
response.encoding = 'utf-8'
return response.text


@run_time
# 使用apparent_encoding根据网页内容分析编码
def getHtml2(url):
response = requests.get(url)
response.encoding = response.apparent_encoding
return response.text


# 运行 n 次平均时间
def get_runs_times(func,url, times=10):
runtime_list = []
for i in range(times):
runtime = func(url)
runtime_list.append(runtime)
print(f'{func.__name__} runs {times} times:{sum(runtime_list) / times}s')


url = 'https://salling.com.cn/'
get_runs_times(getHtml,url, 100)
get_runs_times(getHtml2,url, 100)

1
2
>>>getHtml runs 100 times:0.19394637107849122s
>>>getHtml2 runs 100 times:0.46304878234863284s

结论

  答案很明显手动指定编码格式运行100次的平均时间更少,甚至连使用apparent_encoding根据网页内容分析编码方式的1/2都不到。手动指定编码格式只需要你查看网页源代码头部的charset就可以省下一半时间,所以我的建议是:如果出现中文乱码,手动指定编码格式即可。

其他

  细心的小伙伴应该会发现在时间装饰器中,使用了@wraps(func),它是使用 from functools import wraps 导入的,那么它有什么作用那?

1
@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。

  这可以让我们在装饰器里面访问在装饰之前的函数的属性。例如在计算运行n次平均时间的函数中我们获取了函数的__name__ 属性,这也就使得这个函数更加通用了。如果去掉装饰器中的@wraps(func)这一句,那么在计算运行n次平均时间的函数中__name__ 属性获取到的就是wrapper了。

  此外在计算运行n次平均时间的函数中我们还使用了f"{}{}{}",也就是f-string,亦称为格式化字符串常量(formatted string literals),是Python3.6新引入的一种字符串格式化方法。f-string在形式上是以 f 或 F 修饰符引领的字符串(f’xxx’或 F’xxx’),以大括号 {} 标明被替换的字段;f-string在本质上并不是字符串常量,而是一个在运行时运算求值的表达式。

1
print(f'{func.__name__} runs {times} times:{sum(runtime_list) / times}s')

  在上面的例子中我们使用f-string格式化了函数属性,传递了变量,甚至可直接在其中进行运算。它还有很多有趣的用法,大家快去发掘吧!