次のコードを考えます:
msg = "test"
try:
"a"[1]
except IndexError as msg:
print("Error happened")
print(msg)
誰かがこれがPython 3で次の出力を引き起こす理由を説明できますか?
Error happened
Traceback (most recent call last):
File "test.py", line 6, in <module>
print(msg)
NameError: name 'msg' is not defined
except節のmsg
は、最初の行のmsg
と同じスコープ内にあります。
しかし Python 3にはこの新しい動作もあります :
as target
を使用して例外が割り当てられると、except節の終わりで例外がクリアされます。これはあたかもexcept E as N: foo
に翻訳されました
except E as N: try: foo finally: del N
これは、except句の後に例外を参照できるように、例外を別の名前に割り当てる必要があることを意味します。例外は、トレースバックがアタッチされると、スタックフレームとの参照サイクルを形成し、次のガベージコレクションが発生するまでそのフレーム内のすべてのローカルを維持するため、クリアされます。
そのため、例外ハンドラで「msg
」を上書きし、ハンドラを終了すると変数が削除され、トレースバック参照サイクルがクリアされます。
はい、例外が発生し、msg
が新しい例外オブジェクトに割り当てられるとすぐに、元のオブジェクトには参照がなくなるため、削除されます。新しい例外オブジェクトも、except
ブロックを離れるとすぐに削除されます。
オブジェクトの__del__
メソッドとmsg
に割り当てられた例外をオーバーライドすることで確認できます。
class A:
def __del__(self):
print('object deleted')
class E(Exception):
def __del__(self):
print('exception deleted')
msg = A()
try:
raise E()
except E as msg:
print("Error happened")
この出力:
object deleted
Error happened
exception deleted
NameError: name 'msg' is not defined
例外ブロックは、ブロックの最後でキャッチされた変数を削除しますが、独自のスコープはありません。したがって、イベントのシーケンスは次のとおりです。
1)msg
はローカルスコープ内の文字列に設定されます
2)msg
は1と同じローカルスコープ内のIndexErrorオブジェクトに設定されます
3)msg
は、例外ブロックの終了時にローカルスコープから削除されます
4)msg
はローカルスコープで定義されなくなったため、それにアクセスしようとすると失敗します