PEP-3109:Python 3000での例外の発生
Python 2.xでは、次の2つの同等の方法で例外を発生させることができます。
raise E, V raise E(V)
どちらの場合も、Eが例外クラスでVが値です。しかし、Python 3.0では2番目の形式のみを使用できます。Python 2.xでは、任意のトレースバックを使って例外を発生させるもう1つの(めったに使われない)方法もサポートされています。
raise E, V, T
Python 3.0では、これは次のようになります。
e = E(V) e.__traceback__ = T raise E
Python 3.0の形式の方が煩雑ですが、この形式で例外を発生させることはめったにないので、重要ではありません(例外に任意のトレースバックを付けるような用途が、私には1つも思い浮かびません)。exceptまたはfinallyブロック内でアクティブな例外を再度発生させたい場合は、引数を付けずにraiseを使い続けます。
ジェネレータには、raise E, V, T形式と同じように、3つの引数を受け取るthrow()メソッドを使って例外を発生させる非同期の機能がありました。Python 3.0では、throw()は1つの例外引数しか受け取らないので、raiseと一貫性が保たれています。
PEP-3110:Python 3000での例外の捕捉
Python 2.xでは、例外の捕捉の構文とセマンティクスにいくつかの問題があります。例えば、例外型とそのインスタンスを捕捉するには、次のようなセマンティクスを使います。
except <expression_1>, <expression_2>:
2つの例外型を捕捉する必要がある場合は、次のように型をかっこで囲むことができました。
except (<expression_1>, <expression_2>):
困ったことに、かっこを付け忘れると、この構文が厄介なバグを引き起こすことがあります。次のコードでは、except句でExceptionとstrを(かっこを付けずに)捕捉しています。本来の意図は、汎用Exceptionクラスまたはstr型を捕捉することです。しかし、その意図に反して、実際はException型のみが捕捉されます。Exceptionの特定のインスタンス(この例ではDivisionInZeroException)がstrに代入されます。str()を後から使おうとすると、意味不明で悲惨な結果に終わります。
>>> try:
... 3 / 0
... except Exception, str:
... print repr(str)
...
ZeroDivisionError('integer division or modulo by zero',)
>>> str(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'exceptions.ZeroDivisionError' object is not callable
この例外処理コードがたまたまコードのどこか奥深くに埋もれていて、str()がずっと後で使われていたら、見つけ出すのは容易なことではありません。
Python 3.0では、最初の形式(except TYPE, INSTANCE)を次の形式に置き換えて、これらの問題に対処しています。
except <exception type> as <instance>
さらに、例外型はexceptions.BaseExceptionから派生していなければなりません。複数の例外型を捕捉する場合は、今までどおりタプル構文を使うことができます。Python 3.0で例外を捕捉する方法の例を次に示します。
>>> try:
... 3 / 0
... except Exception as e:
... print(repr(e))
...
ZeroDivisionError('int division or modulo by zero',)
>>> try:
... 3 / 0
... except (Exception, ZeroDivisionError):
... pass
...
例外の捕捉に関連するもう1つの変更点は、sys.exc_type、sys.exc_value、およびsys.exc_tracebackが削除されたことです。情報は、次のようにsys.exc_info()から返されるタプルとして得られます。
>>> try:
... 3/0
... except:
... print(sys.exc_info())
...
(<class 'ZeroDivisionError'>, ZeroDivisionError('int division or modulo by zero',),
<traceback object at 0x3bcbc0>)
将来はsys.exc_info()も削除される可能性があります。exception属性などの代替手段も検討されましたが、既存のコードの自動修正が難しいという理由で見送られました。次のセクションで説明するように、tracebackは属性になりました。
さらに、Python 3.0ではガベージコレクションの問題を回避するために、except句の最後で例外インスタンスの削除を確認します。
