15
exc = None
try:
    raise Exception
except Exception as exc:
    pass

# ...

print(exc)

NameError: 名前 'exc' が定義されていません

これは Python2 で機能していました。なぜこのように変更されたのですか?excクラスレベルの属性と同様に、少なくとも に再割り当てできれば

class Foo(object):
    Bar = Bar

しかし、これでも機能しません。

exc = None
try:
    raise Exception
except Exception as exc:
    exc = exc

同じことを達成するための良いヒントはありますか? 次のようなことは書きたくない。

exc = None
try:
    raise Exception("foo")
except Exception as e:
    exc = e

# ...

print(exc)
4

1 に答える 1

21

このtryステートメントは、バインドされた例外のスコープを明示的に制限して、循環参照がリークを引き起こすのを防ぎます。tryステートメントのドキュメントを参照してください。

as target を使用して例外が割り当てられている場合、それは except 句の最後でクリアされます。

[...]

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

鉱山を強調します。唯一のオプションは、例外を新しい名前にバインドすることであることに注意してください。

Python 2 では、例外はトレースバックへの参照を持っていなかったため、これが変更されました。

ただし、Python 2 でも、トレースバックのクリーンアップについて明示的に警告されます。以下を参照してくださいsys.exc_info()

警告:例外を処理している関数内のローカル変数にトレースバックの戻り値を代入すると、循環参照が発生します。これにより、同じ関数内のローカル変数またはトレースバックによって参照されるものがガベージ コレクションされるのを防ぐことができます。ほとんどの関数はトレースバックにアクセスする必要がないため、最良の解決策はexctype, value = sys.exc_info()[:2]、例外の型と値のみを抽出するようなものを使用することです。トレースバックが必要な場合は、使用後に必ず削除するか (try ... finallyステートメントで行うのが最適です) exc_info()、例外を処理しない関数を呼び出すようにしてください。

例外を再バインドする場合は、トレースバックを明示的にクリアすることをお勧めします。

try:
    raise Exception("foo")
except Exception as e:
    exc = e
    exc.__traceback__ = None
于 2014-06-17T19:23:12.993 に答える