web-dev-qa-db-ja.com

「Foo as bar」を除くと、「bar」がスコープから削除されます

次のコードを考えます:

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
42
knipknap

except節のmsgは、最初の行のmsgと同じスコープ内にあります。

しかし Python 3にはこの新しい動作もあります

as targetを使用して例外が割り当てられると、except節の終わりで例外がクリアされます。これはあたかも

except E as N:
    foo

に翻訳されました

except E as N:
    try:
        foo
    finally:
        del N

これは、except句の後に例外を参照できるように、例外を別の名前に割り当てる必要があることを意味します。例外は、トレースバックがアタッチされると、スタックフレームとの参照サイクルを形成し、次のガベージコレクションが発生するまでそのフレーム内のすべてのローカルを維持するため、クリアされます。

そのため、例外ハンドラで「msg」を上書きし、ハンドラを終了すると変数が削除され、トレースバック参照サイクルがクリアされます。

51
Uku Loskit

はい、例外が発生し、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
33
blhsing

例外ブロックは、ブロックの最後でキャッチされた変数を削除しますが、独自のスコープはありません。したがって、イベントのシーケンスは次のとおりです。

1)msgはローカルスコープ内の文字列に設定されます

2)msgは1と同じローカルスコープ内のIndexErrorオブジェクトに設定されます

3)msgは、例外ブロックの終了時にローカルスコープから削除されます

4)msgはローカルスコープで定義されなくなったため、それにアクセスしようとすると失敗します

7
Michael