【问题标题】:Exception handling in python using class使用类在python中处理异常
【发布时间】:2018-10-19 10:45:46
【问题描述】:

我对 python 异常有疑问。 下面的代码取自python文档,我一度感到困惑。如果有人可以提供帮助,将不胜感激。 这里代码给出的输出为:

B C D

class B(Exception):
    pass
class C(B):
    pass
class D(C):
    pass
for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

如果我更改了以下代码以外的部分代码: 输出将是:

B B B

class B(Exception):
    pass
class C(B):
    pass
class D(C):
    pass
for cls in [B, C, D]:
    try:
        raise cls()
    except B:
        print("B")
    except C:
        print("C")
    except D:
        print("D")

当我在没有 try 块的情况下运行此代码时,如下所示:

class B(Exception):
    pass
class C(B):
    pass
class D(C):
    pass
for cls in [B, C, D]:
    raise cls()

这里的输出是:

Traceback (most recent call last):
  File "C:/Users/885710/Documents/PY/ErrorHandling.py", line 12, in <module>
    raise cls()
B

以下代码类似:

class B(Exception):
    pass
class C(B):
    pass
class D(C):
    pass
for cls in [C,B, D]:
    raise cls()

输出是这个

Traceback (most recent call last):
  File "C:/Users/885710/Documents/PY/ErrorHandling.py", line 12, in <module>
    raise cls()
C

我很困惑,因为如果我单独运行此代码,那么它会输出 B 或 C 或 D 那么为什么在我的第二个代码 sn-p 中输出为

B
B
B

尽管为所有 3 个 B、C、D 类定义了 except

【问题讨论】:

  • C 是 B, D 是 C(又是B),所以(传递性地)D is B(这就是继承的全部意义)。您尝试处理异常的顺序 很重要(在这种情况下)。可能您应该从 Exception 扩展所有类(而不是像现在一样从另一个类扩展),并且您会在第二个 sn 中获得所需的行为-p(不改变其他场景)。 class C(Exception):, class D(Exception):.
  • 也许你应该从 Exception 扩展你所有的类>>你能用例子解释一下吗?
  • 如果 D 是 C,C 是 B,因此 D 是 B,那么为什么它在第一个 sn-p 中打印 B,C,D 而在第二个中打印 B,B,B。还是没看懂
  • 因为 is 只有一种方式:C is B,但 B 不是 C.
  • 好的,谢谢你.. 明白了。

标签: python python-3.x exception-handling


【解决方案1】:

Python's documentation 说:

except 子句中的类与异常兼容,如果它是 同一个类或基类

因此给出的代码:

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

可以简化为:

for cls in [B, C, D]:
    foo = cls()
    if isinstance(foo, D):
        print("D")
    elif isinstance(foo, C):
        print("C")
    elif isinstance(foo, B):
        print("B")

那么你的修改会变成:

for cls in [B, C, D]:
    foo = cls()
    if isinstance(foo, B):
        print("B")
    elif isinstance(foo, C):
        print("C")
    elif isinstance(foo, D):
        print("D")

因此,无论foo 是B、C 还是D 的实例,它都会满足第一种情况,因为isinstance 也会为子类的实例产生True。

【讨论】:

  • 感谢您添加了更多解释。很好的帮助!
【解决方案2】:

由于B 是C 和D 的超类,您的第二个版本将始终使用B 的第一个except 块。因为 Python 运行时会从上到下搜索匹配的 except 块。如果异常是except 块中的类的实例,则except 块匹配。如果你抛出一个实例 C 例如这个块将匹配,因为 C() 是(也是)B 的一个实例。

根据经验,except 语句必须从最具体的条件减少到最一般的条件,例如。 g.:

try:
    throw ...
except D: # the most specific class
    print("D")
except C: # is more specific than B but less than D
    print("C")
except B: # the most general class in your hierarchy
    print("B")
except BaseException as e: the most general exception class
    print(e.__class__.__name__)

【讨论】:

  • 太棒了!非常感谢您的解释。
【解决方案3】:

我们先来了解一下这个代码示例所涉及的类层次结构

class B(Exception):
    pass
class C(B):
    pass
class D(C):
    pass

B -> 基类

C -> 继承自 B

D -> 继承自 C 和 C -> 继承自 B

因此 D -> 继承自 B 和 C

一条 try 语句可能有多个用于不同异常的 except 子句。但是最多会执行一个 except 子句

除非您正在捕获引发的异常,否则基类将被优先捕获。即,当异常类被继承时,except 中的优先级被赋予 BASE 类(在您的代码中它是类B)。

现在是第一种情况:

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

Iteration 1 : 元素:B

第一个控件检查except D,因为它不是基类,也不是提升类的匹配类,然后控件将移动到except C,最后它将执行except B,从而打印B。

Iteration 2 : 元素:C

第一个控件检查except D,因为它不是基类,也不是提升类的匹配类,然后控件将移动到except C并执行except C,从而打印C

Iteration 3 : 元素:D

首先控制检查except D,因为它是一个匹配的类,然后控制将 执行except D,从而打印D

现在考虑第二种情况:

for cls in [B, C, D]:
    try:
        raise cls()
    except B:
        print("B")
    except C:
        print("C")
    except D:
        print("D")

这里B 是类C 和D 的基类,因此当您在堆栈顶部写入except B 时,控件不会转到后续的except C 和except D。因此,对于 for 循环的每次迭代,它都会将输出打印为 B。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多