7

このコードがあります:

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警告が出力されることがあるのか​​ということです。カスタム例外に何か問題がありますか?

4

3 に答える 3

12

__del__どこかでフックの誤動作が見られます 。

Pythonインタープリターが終了しているため、シャットダウン中にTypeErrorスローされます。__del__

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

以下を追加することで、Python を終了せずにエラーをトリガーできます。

MyException = None
del a

ジェネレーターの残りを使用して消費するか、 Python が終了して削除する前にlist(a)明示的にジェネレーターを閉じると、エラー メッセージは表示されなくなります。a.close()MyException

別の回避策は、GeneratorExit最初に処理することです。

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

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

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

Pythonでのフックとその他のグローバル オブジェクトの相互作用は、ドキュメントに大きな赤い警告とともに記載されていることに注意.__del__()てください。.__del__()

警告: メソッドが呼び出される不安定な状況のため__del__()、実行中に発生した例外は無視され、sys.stderr代わりに警告が出力されます。また、__del__()モジュールの削除に応答して が呼び出された場合 (たとえば、プログラムの実行が完了したとき)、__del__()メソッドによって参照されている他のグローバルが既に削除されているか、取り壊されている途中である可能性があります (インポート機構のシャットダウンなど)。 )。このために、__del__()メソッドは、外部不変条件を維持するために必要な絶対最小値を実行する必要があります。バージョン 1.5 以降、Python は、名前が単一のアンダースコアで始まるグローバルが、他のグローバルが削除される前にモジュールから削除されることを保証します。__del__()そのようなグローバルへの他の参照が存在しない場合、これは、メソッドが呼び出されたときにインポートされたモジュールがまだ使用可能であることを保証するのに役立ちます。

于 2013-08-10T16:07:40.023 に答える
2

Windows の Python 3.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 FooErrorするとfrom 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)
于 2014-09-11T06:10:00.243 に答える
1

私は同じ問題を抱えていましたが、例外クラスへのインポートがありませんでした。そのため、インタープリターは except 句のクラスを解決しませんでした。

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

于 2015-08-11T21:35:12.720 に答える