Python中异常处理的开销
在另一个问题中,接受的答案建议用一个try/except块来替代Python代码中的一个(非常简单的)if语句,以提高性能。
抛开编码风格的问题,假设这个异常从来不会被触发,那么有一个异常处理器和没有异常处理器,以及用一个比较是否为零的if语句相比,性能上到底差别有多大呢?
5 个回答
28
在Python 3.11版本中,
实现了“零成本”的异常处理。当没有发生异常时,使用try语句的开销几乎被消除了。
https://docs.python.org/3.11/whatsnew/3.11.html#optimizations
82
这个问题其实在设计与历史常见问题解答中已经回答过了:
如果没有出现异常,使用try/except代码块是非常高效的。实际上,捕获一个异常是比较耗费资源的。
153
你为什么不使用timeit
模块来测量一下呢?这样你就能看到它对你的应用程序是否有影响。
好吧,我刚刚尝试了以下代码(在Windows 11上使用Python 3.11.1):
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.06 usec/pass
a = 1
if a:
b = 10/a
0.05 usec/pass
a = 1
b = 10/a
0.03 usec/pass
a = 0
try:
b = 10/a
except ZeroDivisionError:
pass
0.27 usec/pass
a = 0
if a:
b = 10/a
0.02 usec/pass
a = 0
b = 10/a
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
File "C:\Python311\Lib\timeit.py", line 178, in timeit
timing = self.inner(it, self.timer)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<timeit-src>", line 6, in inner
ZeroDivisionError: division by zero
如你所见,使用try/except
语句和直接用if
语句之间的差别不大,除非真的触发了异常。(当然,如果没有任何控制结构,速度是最快的,不过差别不大,如果出现问题程序会崩溃)。
再看看2010年的结果:
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
我现在用的电脑速度大约是那时候的两倍。处理异常的成本看起来是一样的,而“正常”的操作(算术运算)比控制结构的处理速度提升得还要多,但多年前的观点依然成立:
这些操作的速度都在同一个数量级上,基本上不会有什么影响。只有在条件真的满足(经常会发生)时,if
版本的速度才会明显更快。