【问题标题】:how are exceptions compared in an except clause如何在 except 子句中比较异常
【发布时间】:2009-05-12 03:20:08
【问题描述】:

在以下代码段中:

try:
    raise Bob()
except Fred:
    print "blah"

Bob 和 Fred 的比较是如何实现的?

从玩起来似乎在下面调用isinstance,这样对吗?

我问是因为我试图颠覆这个过程,特别是我希望能够构建一个 Bob 以便它被 execpt Fred 捕获,即使它实际上不是 Fred 或其任何子类的实例.

有几个人问我为什么要这样做......

我们有一个 RMI 系统,它是围绕使其尽可能无缝的理念构建的,这里有一个使用中的简单示例,注意 RMI 系统中没有特定于套接字的代码,套接字只是提供了一个方便的例子。

import remobj
socket = remobj.RemObj("remote_server_name").getImport("socket")
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", 0))
print "listening on port:", s.getsockname()[1]
s.settimeout(10)
try:
    print "received:", s.recv(2048)
except socket.timeout:
    print "timeout"

现在,在这个特定示例中,except 没有按预期工作,因为引发的对象不是 socket.timeout 的实例,它是我们的代理助手类之一的实例。

【问题讨论】:

  • 你到底为什么要这么做?
  • -1:颠覆 Python 明显、明确的功能,故意混淆未来的维护者。

标签: python exception


【解决方案1】:

我相信您对比较如何进行的猜测是正确的,唯一的拦截方法是将 Fred 作为基类添加到 Bob。例如:

# Assume both Bob and Fred are derived from Exception
>>> class Bob(Bob, Fred):
...     pass
... 
>>> try:
...     raise Bob()
... except Fred:
...     print 'blah'
blah

据我所知,这是使它在您编写时工作的唯一方法。但是,如果您只是将 except: 行重写为

... except (Bob, Fred):

它将捕获 Bob 和 Fred,而无需修改 Bob 的定义。

【讨论】:

    【解决方案2】:

    我希望能够构建一个 Bob 这样它就会被 execpt 捕获 弗雷德即使它实际上不是 Fred 或其任何一个实例 子类。

    好吧,你可以捕捉到“异常”——但这不是很 Python。您应该尝试捕获正确的异常,然后作为最后的手段使用一般异常(所有异常都是从其子类化)。如果这对您不起作用,那么您的设计阶段出现了严重错误。

    See this note from Code Like A Pythonista

    注意:始终指定例外情况 抓住。切勿使用裸 except 子句。 光秃秃的 except 子句会抓住 意外的异常,使您的 代码极难调试。

    然而,Python 之禅中的成语之一是

    特殊情况不足以打破规则。 虽然实用胜于纯粹。

    【讨论】:

      【解决方案3】:
      >>> class Fred(Exception):
          pass
      >>> class Bob(Fred):
          pass
      >>> issubclass(Bob, Fred)
      True
      >>> issubclass(Fred, Bob)
      False
      >>> try:
          raise Bob()
      except Fred:
          print("blah")
      
      
      blah
      

      所以基本上捕获了异常,因为 Bob 是 Fred 的子类,我假设,要么他们必须实现类似于 issubclass(Bob, Fred) 的逻辑

      看,这就是你想要的实现方式,随你所愿。当然不是在 init 中,而是在其他一些方法中。

      >>> class Bob(Exception):
          def __init__(self):
              raise Fred
      
      >>> try:
          b = Bob()
      except Fred:
          print('blah')
      
      
      blah
      

      【讨论】:

        【解决方案4】:

        我不清楚在 socket.timeout 中隐藏您的异常如何符合“无缝”理念?捕获定义的预期异常有什么问题?

        try:
            print "received:", s.recv(2048)
        except socket.timeout:
            print "timeout"
        except our_proxy_helper_class:
            print 'crap!'
        

        或者,如果您真的想将其捕获为 socket.timeout,为什么不在 our_proxy_helper_class 中提高 socket.timeout 呢?

        raise socket.timeout('Method x timeout')
        

        因此,当您在 our_proxy_helper_class 中引发 socket.timeout 时,它应该被 'except socket.timeout' 捕获。

        【讨论】:

        • 无缝我的意思是本地和远程代码之间唯一可见的区别应该是导入。如果您使用“import socket”在本地导入了套接字,那么您将在 socket.timeout 上除外,因此理想情况下您应该在远程导入时执行相同操作。
        • 在示例中,socket.timeout 不是一个类。只要您不进行任何自省,它的外观和行为就像一个类。但它实际上是另一个代理助手实例。我现在相信这是核心问题,我正在研究元类作为将它变成实际类的一种方式。然后我将能够将异常作为该类的实例引发,并且一切都应该按预期工作。
        【解决方案5】:

        至少在 CPython 中,看起来有一个类型为 10 的 COMPARE_OP 操作(异常匹配)。你不可能做任何事情来破解这个计算。

        >>> 导入磁盘 >>> def foo(): ... 尝试: ... 引发 OSError() ...除了例外,e: ... 经过 ... >>> dis.dis(foo) 2 0 SETUP_EXCEPT 13(至 16) 3 3 LOAD_GLOBAL 0(操作系统错误) 6 CALL_FUNCTION 0 9 RAISE_VARARGS 1 12 POP_BLOCK 13 JUMP_FORWARD 21(至 37) 4 >> 16 DUP_TOP 17 LOAD_GLOBAL 1(例外) 20 COMPARE_OP 10(异常匹配) 23 JUMP_IF_FALSE 9(至 35) 26 POP_TOP 27 POP_TOP 28 STORE_FAST 0 (e) 31 POP_TOP 5 32 JUMP_FORWARD 2(至 37) >> 35 POP_TOP 36 结束_最终 >> 37 LOAD_CONST 0(无) 40 RETURN_VALUE

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-17
          • 1970-01-01
          • 2015-11-17
          • 1970-01-01
          • 2021-04-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多