Python FAQ:"异常有多快?"

pok*_*oke 14 python performance exception try-catch

我只是在看Python常见问题,因为它在另一个问题中提到过.我之前从未真正详细地看过它,但我遇到了这个问题:"异常有多快?":

try/except块非常有效.实际上捕获异常是昂贵的.在2.0之前的Python版本中,通常使用这个习惯用法:

try:
    value = mydict[key]
except KeyError:
    mydict[key] = getvalue(key)
    value = mydict[key]
Run Code Online (Sandbox Code Playgroud)

我对"捕获异常是昂贵的"部分感到有些惊讶.这是指那些except实际将异常保存在变量中的情况,或者通常是所有excepts(包括上例中的那个)的情况?

我一直认为使用这样的习语会非常pythonic,特别是在Python中"请求宽恕比获得权限更容易".关于SO的许多答案通常都遵循这个想法.

捕获异常的性能真的那么糟糕吗?在这种情况下,是否应该遵循LBYL("在你跳跃之前看")?

(请注意,我并没有直接讨论FAQ中的示例;还有许多其他示例,您只需注意异常而不是之前检查类型.)

Mar*_*nen 20

捕获异常昂贵的,但异常应该是例外(读取,不经常发生).如果例外很少,try/catch则比LBYL快.

以下示例使用异常和LBYL对字典键进行查找,此时密钥存在且不存在时:

import timeit

s = []

s.append('''\
try:
    x = D['key']
except KeyError:
    x = None
''')

s.append('''\
x = D['key'] if 'key' in D else None
''')

s.append('''\
try:
    x = D['xxx']
except KeyError:
    x = None
''')

s.append('''\
x = D['xxx'] if 'xxx' in D else None
''')

for i,c in enumerate(s,1):
    t = timeit.Timer(c,"D={'key':'value'}")
    print('Run',i,'=',min(t.repeat()))
Run Code Online (Sandbox Code Playgroud)

产量

Run 1 = 0.05600167960596991       # try/catch, key exists
Run 2 = 0.08530091918578364       # LBYL, key exists (slower)
Run 3 = 0.3486251291120652        # try/catch, key doesn't exist (MUCH slower)
Run 4 = 0.050621117060586585      # LBYL, key doesn't exist
Run Code Online (Sandbox Code Playgroud)

当通常情况也不例外try/catch时,与LBYL相比,"非常有效".

  • 做一些快速数学运算,数字表示只有当你期望在最多8.95%的时间内找不到密钥时才会使用try/except(11个呼叫中约有1个).我没有足够的空间来推导我使用的等式但是:让`cbn` =正常情况下的分支方法成本,`cbx` =特殊情况下分支方法的成本,`ctn` =尝试成本/正常情况除外case,`ctx` = try的成本/除特殊情况外,px =异常情况发生的概率; 那么对于所有`px <=(ctn-cbn)/(ctn-ctx + cbx-cbn)`,try/except方法在长期内会更快. (3认同)

Cat*_*lus 6

显然,成本取决于实施,但我不担心.无论如何,它不太可能重要.标准协议会在最奇怪的地方引发异常(想想StopIteration),所以无论你喜欢与否,你都会被提升和捕捉.

在LBYL和EAFP之间进行选择时,要担心代码的可读性,而不是关注微优化.如果可能的话,我会避免类型检查,因为它可能会降低代码的通用性.