Python FAQ:「例外はどれほど速いのですか?」

私は別の質問で言及されているので、Python FAQを見ていただけです。これまでのところ詳細を見たことはありませんでしたが、この質問:「例外はどのくらい速いのですか?」:

try/exceptブロックは非常に効率的です。実際に例外をキャッチするのは高価です。 2.0より前のバージョンのPythonでは、このイディオムを使用するのが一般的でした:

  try:
    値= mydict [キー]
KeyErrorを除く:
    mydict [key] = getvalue(key)
    値= mydict [キー]
 

「例外をキャッチするのは高価です」の部分についてちょっと驚きました。これは実際に例外を変数に保存する場合、または一般的にすべての except (上記の例を含む)の場合を除いて、これらの except

私は常に、このようなイディオムを使用することは、特にPython "許しを得るのが許諾を得るのが簡単です" のように非常に非常態であると思っていました。 SOに関する多くの答えも、一般にこの考え方に従います。

例外をキャッチするためのパフォーマンスは本当に悪いですか?このような場合、LBYL(「飛び越す前に見てください」)に従うべきでしょうか?

(私はFAQの例について直接言及しているわけではありませんが、これまでに型を調べる代わりに例外を調べる例がたくさんあります)。

13
追加された 編集された
ビュー: 1

3 答え

例外をキャッチするのは高価ですが、例外は例外的(読んでも頻繁ではありません)にする必要があります。例外がまれである場合、 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 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

通常の場合も例外ではなく、 try/catch はLBYLと比較して "非常に効率的"です。

19
追加された
"例外は例外的でなければならない"良いマントラです。私はあまりにも頻繁に忘れる。
追加された 著者 kevinarpe,
かなり私は何を言うつもりだった。これは例外を使用して、実際に高価な通常のプログラムフローを制御しています。
追加された 著者 Tony Hopkinson,
いくつかの素早い計算を行うと、数字はtry/exceptが使用されるべきであることを示します。キーが8.95%(11回の呼び出しで約1時間)で見つからないことが予想される場合にのみ使用してください。私は使用した方程式を導き出す余地がありませんが、通常の場合は cbn =分岐方法のコスト、例外的には分岐方法のコスト cbx code> ctn =通常の場合のtry/exceptのコスト、例外的にはtry/exceptのコスト、例外的ケースの発生確率はpxです。すべての px <=(ctn-cbn)/(ctn-ctx + cbx-cbn)に対して、try/exceptメソッドは長期的には高速になります。
追加された 著者 Eli Collins,
ちょうど補遺 - これらの種類のものを測定して計算すること自体が時間がかかるので、私は1/8倍以上発生するものは "例外的な"ケースではないというパイソンの経験則を使用しようとします。おそらく別のメソッドを使うべきでしょう。これは、APIを設計するときやコードを書くときにも有用であることが証明されています。
追加された 著者 Eli Collins,

コストは実装に依存しますが、明らかに心配しません。とにかく問題になることはまずありません。標準的なプロトコルでは、最も奇妙な場所(例外的に StopIteration )で例外が発生するため、気に入っているかどうかにかかわらず、

LBYLとEAFPのどちらかを選択するときは、マイクロ最適化に焦点を当てるのではなく、コードの可読性を心配してください。可能であれば、コードの一般性を低下させる可能性があるので、型チェックを避けたいと思います。

6
追加された
はい、時期尚早の最適化は避けてください。
追加された 著者 Brecht Machiels,

キーが見つからない場合は例外的ですが、私はすべてのケースで一定の速度を提供する 'get'メソッドを使用することをお勧めします:

s.append('''\
x = D.get('key', None)
''')

s.append('''\
x = D.get('xxx', None)
''')
0
追加された
代わりに dict.setdefault を使用することができます。しかし、やはりコードはFAQの単なる例に過ぎませんでした。
追加された 著者 poke,
申し訳ありませんが、実際は遅いです。
追加された 著者 Johan Boulé,