ジェネレータのthrowメソッドを使用している場合ではなく、例外TypeError警告が表示される場合があります

Source python python-3.x exception generator

このコードがあります:
class MyException(Exception):
  pass

def gen():
  for i in range(3):
    try:
      yield i
    except MyException:
      print("MyException!")


a = gen()
next(a) 
a.throw(MyException)

このコードの実行:
$ python3.3 main.py
MyException!
$ python3.3 main.py
MyException!
Exception TypeError: TypeError('catching classes that do not inherit from BaseException is not allowed',) in <generator object gen at 0xb712efa4> ignored
$ python3.3 main.py
MyException!
$ python3.3 main.py
MyException!
$ python3.3 main.py
MyException!
Exception TypeError: TypeError('catching classes that do not inherit from BaseException is not allowed',) in <generator object gen at 0xb714afa4> ignored

私が理解していないのは、なぜこのException TypeError警告が出力されることがあるのか​​ということです。カスタム例外に何か問題がありますか?
推奨答え
どこかで__del__フックが誤動作しているのがわかります。

Pythonインタープリターが終了しているため、シャットダウン中にTypeErrorがスローされ、すべてが削除され、__del__デコンストラクターフックでスローされた例外はすべて無視されます(ただし、出力されます)。

終了時に、PythonはすべてをNoneに再バインドすることで名前空間内のすべてをクリアしますが、これが発生する順序は設定されていません。まだ実行中のジェネレーターは削除されると閉じられ(a.close()が呼び出されます)、ジェネレーターでGeneratorExit例外がトリガーされ、Pythonがexcept MyException:行に対してテストします。ただし、MyExceptionがすでにクリアされていて、Pythonがexcept None:を検出した場合、TypeErrorがスローされ、そのメッセージが出力されます。

次を追加することで、Pythonを終了せずにエラーをトリガーできます。
MyException = None
del a
list(a)を使用してジェネレーターの残りの部分を消費するか、Pythonが終了してa.close()を削除する前に、MyExceptionを使用してジェネレーターを明示的に閉じると、エラーメッセージは表示されなくなります。

別の回避策は、最初にGeneratorExitを処理することです。
def gen():
  for i in range(3):
    try:
      yield i
    except GeneratorExit:
      return
    except MyException:
      print("MyException!")

Pythonは次のexceptハンドラーを評価しません。

このエラーはPython3.2以前では再現できないため、hash randomization(Python 3.3で導入)がオブジェクトがクリアされる順序をランダム化するように見えます。これは確かに、一部の実行でのみエラーが表示され、ハッシュ順序が修正された以前のPython実行では表示されない理由を説明しています。

Pythonでの.__del__()フックと他のグローバルオブジェクトの相互作用は、 .__del__() documentationに大きな赤い警告とともに文書化されていることに注意してください。

警告:__del__()メソッドが呼び出されるという不安定な状況のため、実行中に発生した例外は無視され、代わりに警告がsys.stderrに出力されます。また、モジュールの削除に応答して__del__()が呼び出された場合(たとえば、プログラムの実行が完了した場合)、__del__()メソッドによって参照される他のグローバルは、すでに削除されているか、破棄されている可能性があります(たとえば、インポート機構)。シャットダウン)。このため、__del__()メソッドは、外部不変条件を維持するために必要な最小限の処理を実行する必要があります。バージョン1.5以降、Pythonは、名前が1つのアンダースコアで始まるグローバルが、他のグローバルが削除される前にモジュールから削除されることを保証します。そのようなグローバルへの他の参照が存在しない場合、これは、__del__()メソッドが呼び出されたときにインポートされたモジュールが引き続き使用可能であることを保証するのに役立つ場合があります。
その他答え 1
Windows上のPython3.3でも同じエラーが発生していましたが、独自のファイルで例外を定義していた点が異なります。これらは私のコードファイルでした:
$ cat FooError.py 
class FooError(Exception):
    pass

$ cat application.py
import FooError
try:
    raise FooError('Foo not bar!')
Except FooError as e:
    print(e)

これは私が得ていた例外でした:

TypeError:BaseExceptionから継承しないクラスをキャッチすることは許可されていません。

import FooErrorfrom FooError import *に変更すると、問題が解決しました。わかりやすくするために、最終的なコードは次のとおりです。
$ cat FooError.py 
class FooError(Exception):
    pass

$ cat application.py
from FooError import *
try:
    raise FooError('Foo not bar!')
Except FooError as e:
    print(e)
その他答え 2
同じ問題が発生しましたが、例外クラスへのインポートがありませんでした。そのため、インタプリタは、except節のクラスを解決しませんでした。

したがって、インポートを追加するだけで、うまくいけばすべてが機能します。

関連記事

VimはPythonで視覚的な選択範囲の間の文字列を取得します
Tastypie移行エラー
Tastypie移行エラー
Pythonで、可変長の組み合わせまたは順列を作成するにはどうすればよいですか?
2つの辞書を組み合わせて、文字列値を連結しますか?