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

scd*_*dmb 7 python exception generator python-3.x

有这个代码:

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)
Run Code Online (Sandbox Code Playgroud)

运行此代码:

$ 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
Run Code Online (Sandbox Code Playgroud)

我不明白的是为什么有时打印这个Exception TypeError警告.自定义异常有问题吗?

Mar*_*ers 12

你看到一个 __del__钩子在某处行为不端.

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

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

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

MyException = None
del a
Run Code Online (Sandbox Code Playgroud)

如果您使用list(a)和使用生成器的其余部分,或者a.close()在Python退出和删除之前显式关闭生成器MyException,则错误消息将消失.

另一种解决方法是先处理GeneratorExit:

def gen():
  for i in range(3):
    try:
      yield i
    except GeneratorExit:
      return
    except MyException:
      print("MyException!")
Run Code Online (Sandbox Code Playgroud)

并且Python不会评估下一个except处理程序.

使用Python 3.2或更早版本无法重现该错误,因此它看起来像散列随机化(在Python 3.3中引入)随机化订单对象被清除; 这肯定解释了为什么你只在你的一些运行中看到错误,而不是在修复哈希顺序的早期Python运行中.

需要注意的是相互作用.__del__()挂钩和与其他的Python全局对象是记录与一个大红色预警.__del__()文档:

警告:由于__del__()调用方法的不稳定情况,将忽略执行期间发生的异常,并打印警告sys.stderr.此外,当__del__()响应于被删除的模块而调用时(例如,当完成程序的执行时),该__del__()方法引用的其他全局变量可能已被删除或正在被拆除(例如,导入机器关闭) ).因此,__del__()方法应该做到维持外部不变量所需的绝对最小值.从版本1.5开始,Python保证在删除其他全局变量之前,从其模块中删除名称以单个下划线开头的全局变量; 如果不存在对此类全局变量的其他引用,这可能有助于确保在__del__()调用方法时导入的模块仍然可用.