关于性能:Python中异常处理程序的成本

Cost of exception handlers in Python

在另一个问题中,公认的答案建议用try/except块替换python代码中的if语句(非常便宜),以提高性能。

撇开编码风格的问题不谈,假设从未触发异常,使用异常处理程序与不使用异常处理程序、使用比较为零的if语句有多大区别?


为什么不使用timeit模块来测量呢?这样您就可以看到它是否与您的应用程序相关。

好的,所以我尝试了以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import timeit

statements=["""\
try:
    b = 10/a
except ZeroDivisionError:
    pass"""
,
"""\
if a:
    b = 10/a"""
,
"b = 10/a"]

for a in (1,0):
    for s in statements:
        t = timeit.Timer(stmt=s, setup='a={}'.format(a))
        print("a = {}
{}"
.format(a,s))
        print("%.2f usec/pass
"
% (1000000 * t.timeit(number=100000)/100000))

结果:

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
a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.25 usec/pass

a = 1
if a:
    b = 10/a
0.29 usec/pass

a = 1
b = 10/a
0.22 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.57 usec/pass

a = 0
if a:
    b = 10/a
0.04 usec/pass

a = 0
b = 10/a
ZeroDivisionError: int division or modulo by zero

因此,正如预期的那样,没有任何异常处理程序的速度稍微快一点(但在异常发生时会在您的脸上爆炸),并且只要不满足条件,try/except就比显式if快。

但它们都在同一数量级内,不太可能以任何方式发生关系。只有在实际满足条件的情况下,if版本才明显更快。


这个问题实际上在设计和历史常见问题解答中得到了解答:

A try/except block is extremely efficient if no exceptions are raised.
Actually catching an exception is expensive.


这个问题有误导性。如果假设从未触发过异常,那么这两种情况都不是最佳代码。

如果假设异常是作为错误条件的一部分触发的,那么您已经超出了需要最佳代码的范围(而且您可能不会在这样的细粒度级别处理它)。

如果你将异常作为标准控制流程的一部分使用——这就是Python式的"请求原谅,而不是许可"方式——那么异常将被触发,并且成本取决于异常的类型、if的类型以及你估计异常发生的时间百分比。