抽象基类和异常

Mik*_*Coy 8 python abc python-3.x

问题

为什么使用该子句Exception创建的抽象的虚拟子类ABCMeta.register不匹配except

背景

我想确保我正在使用的包抛出的异常被转换为MyException,以便导入我的模块的代码可以捕获我的模块抛出的任何异常,except MyException:这样except Exception它们就不必依赖于实现细节(我正在使用第三方包的事实)。

例子

为此,我尝试使用抽象基类注册OtherException为:MyException

# Tested with python-3.6
from abc import ABC

class MyException(Exception, ABC):
    pass

class OtherException(Exception):
    """Other exception I can't change"""
    pass

MyException.register(OtherException)

assert issubclass(OtherException, MyException)  # passes

try:
    raise OtherException("Some OtherException")
except MyException:
    print("Caught MyException")
except Exception as e:
    print("Caught Exception: {}".format(e))
Run Code Online (Sandbox Code Playgroud)

断言通过(如预期),但异常发生在第二个块上:

Caught Exception: Some OtherException
Run Code Online (Sandbox Code Playgroud)

Mik*_*Coy 6

好吧,我又研究了一些。答案是,这是 Python3 中长期悬而未决的开放问题(从第一个版本开始就存在),并且显然是在 2011 年首次报告的。正如Guido在评论中所说,“我同意这是一个错误,应该修复。” 不幸的是,由于对修复性能和一些需要处理的极端情况的担忧,这个错误一直存在。

核心问题是use和 notPyErr_GivenExceptionMatches中的异常匹配例程。由于类型和对象在 python3 中应该是相同的,因此这相当于一个错误。errors.cPyType_IsSubtypePyObject_IsSubclass

我已经对python3 做了一个 PR,似乎涵盖了线程中讨论的所有问题,但考虑到历史,我对它很快就会被合并并不是非常乐观。我们拭目以待。


Meg*_*Ing 5

原因很简单:

from abc import ABC

class MyException(Exception, ABC):
    pass

class OtherException(Exception):
    """Other exception I can't change"""
    pass

MyException.register(OtherException)

assert issubclass(OtherException, MyException)  # passes
assert OtherException in MyException.__subclasses__()  # fails
Run Code Online (Sandbox Code Playgroud)

编辑:这assert模仿了 except 子句的结果,但并不代表实际发生的情况。查看接受答案以获得解释。

解决方法也很简单:

class OtherException(Exception):
    pass
class AnotherException(Exception):
    pass

MyException = (OtherException, AnotherException)
Run Code Online (Sandbox Code Playgroud)