有时会显示异常TypeError警告,使用生成器的throw方法时有时不会显示


问题内容

有以下代码:

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__某个地方的 钩子行为异常。

TypeError而被抛出 关闭 ,如解释器正在退出一切被删除,并且在一个引发的任何异常Python的__del__解构钩被忽略了(但
打印的)。

在退出时,Python通过将所有内容重新绑定到来清除名称空间中的所有内容None,但是未设置发生这种情况的顺序。a.close()删除后,仍在运行的生成器将被关闭(称为),这会触发GeneratorExit生成器中的异常,Python会对您的except MyException:行进行测试。但是,如果MyException已经 被清理和Python看到except None:TypeError被抛出,你看到那个消息打印。

您可以通过添加以下命令来触发错误,而无需退出Python:

MyException = None
del a

如果您使用list(a)并使用了生成器的其余部分,或者a.close()在Python退出和删除之前使用显式关闭了生成器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运行中却看不到错误。

请注意,.__del__()Python中的钩子与其他全局对象的交互在文档中以红色警告警告的形式进行了.__del__()说明

警告
:由于在不稳定的情况下__del__()调用方法,在执行过程中发生的异常将被忽略,并显示警告sys.stderr。同样,当__del__()响应于模块被删除而被调用时(例如,当执行程序时),该__del__()方法引用的其他全局变量可能已经被删除或正在被拆除(例如,进口机械关闭)
)。为此原因,__del__()方法应做到维持外部不变性所需的绝对最小值。从版本1.5开始,Python保证在删除其他全局变量之前,将从其下划线开头的全局变量从其模块中删除。如果不存在对此类全局变量的其他引用,则这可能有助于确保在__del__()调用该方法时导入的模块仍然可用。