【问题标题】:Changing the class of an exception after raising it引发异常后更改其类
【发布时间】:2016-05-06 20:15:02
【问题描述】:

我写了以下代码,它引发了一个异常,而一个线程不断改变引发的异常的类:

import threading
import time

class Foo(Exception):
    pass

class Bar(Exception):
    pass

class MyExc(Exception):
    pass

b = True
e = MyExc()
def mess():
    global e
    global b
    time.sleep(0.00000000000000000000001)
    while b:
        e.__class__ = Bar
        e.__class__ = Foo
threading.Thread(target=mess).start()

try:
    try:
        try:
            try:
                try:
                    raise e
                except Foo:
                    print(1)
            except Foo:
                print(2)
        except Foo:
            print(3)
    except Foo:
        print(4)
except Foo:
    print(5)
except Bar:
    print('bar')
except MyExc:
    print('myexc')

b = False

使用 CPython,不同的时间会使其打印 1barmyexc(您可能需要稍微调整睡眠时间才能获得其中三个)。但绝不是其他号码。

为什么?是不是因为 CPython 在展开堆栈时没有运行其他线程?

【问题讨论】:

  • 我不明白为什么它会打印除 1 之外的任何数字。如果例外是 Foo,将打印 1;为什么会触发任何其他except 块?
  • 因为最初,异常不是 Foo 的实例。但是,我希望线程在 Python 展开堆栈时使其成为 Foo 的实例,触发中间的 except 语句之一

标签: python multithreading exception cpython


【解决方案1】:

我怀疑正在发生的事情是,当您启动线程时,它会在到达 raise 命令之前更改“e”的类型。一旦到达 raise 命令,异常就会在其当前类型(可能是 MyExc、Foo 或 Bar)中被捕获,然后通过异常处理逻辑移动。

如果你想更多地使用它,你可以在 try/except 逻辑中重新引发异常。比如:

try:
    try:
        try:
            raise e
        except:
            print(1)
            raise
    except:
        print(2)
        raise
 except:
     print(3)
     raise

【讨论】:

  • 现在打印myexcbar或所有数字。绝不是数字的子集。
  • 对。我认为一旦最初引发异常,CPython 的内部就会创建异常的新实例并将其传递给异常处理逻辑。这样,您就不能在异常发生时更改异常的类或消息或任何内容。您可以在这里查看异常处理逻辑:github.com/python/cpython/blob/master/Python/errors.c
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 1970-01-01
  • 2021-08-24
  • 1970-01-01
  • 2013-01-23
  • 1970-01-01
  • 2016-04-18
相关资源
最近更新 更多