Python中异常处理程序的成本

问题:Python中异常处理程序的成本

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

除了编码样式问题外,并假设永远不会触发异常,拥有一个异常处理程序与没有一个异常处理程序,与拥有与零比较的if语句相比,在性能方面有何不同?

In another question, the accepted answer suggested replacing a (very cheap) if statement in Python code with a try/except block to improve performance.

Coding style issues aside, and assuming that the exception is never triggered, how much difference does it make (performance-wise) to have an exception handler, versus not having one, versus having a compare-to-zero if-statement?


回答 0

为什么不使用timeit模块进行测量?这样,您可以查看它是否与您的应用程序相关。

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

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 = {}\n{}".format(a,s))
        print("%.2f usec/pass\n" % (1000000 * t.timeit(number=100000)/100000))

结果:

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版本才会明显更快。

Why don’t you measure it using the timeit module? That way you can see whether it’s relevant to your application.

OK, so I’ve just tried the following:

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 = {}\n{}".format(a,s))
        print("%.2f usec/pass\n" % (1000000 * t.timeit(number=100000)/100000))

Result:

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

So, as expected, not having any exception handler is slightly faster (but blows up in your face when the exception happens), and try/except is faster than an explicit if as long as the condition is not met.

But it’s all within the same order of magnitude and unlikely to matter either way. Only if the condition is actually met, then the if version is significantly faster.


回答 1

设计和历史常见问题解答中实际上回答了这个问题

如果没有引发异常,则try / except块非常有效。实际上捕获异常是昂贵的。

This question is actually answered in the Design and History FAQ:

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


回答 2

这个问题是误导的。如果您假设从未触发该异常,那么这两个都不是最佳代码。

如果您假设异常是作为错误条件的一部分而触发的,那么您已经超出了想要最佳代码的范围(而且您可能始终无法像这样细粒度地对其进行处理)。

如果您将异常用作标准控制流程的一部分(这是Pythonic的“询问宽恕,而不是允许”的方式),那么异常将被触发,代价取决于异常的种类,如果,以及您估计发生异常的时间百分比。

This question is misleading. If you assume the exception is never triggered, neither one is optimal code.

If you assume the exception is triggered as part of an error condition, you are already outside the realm of wanting optimal code (and you probably aren’t handling it at a fine-grained level like that anyway).

If you are using the exception as part of the standard control flow – which is the Pythonic “ask forgiveness, not permission” way – then the exception is going to be triggered, and the cost depends on the kind of exception, the kind of if, and what percentage of time you estimate the exception happens.