猴子补丁异常类和其他内建类

4 投票
1 回答
1353 浏览
提问于 2025-04-17 20:52

无论是什么类型的错误,我都想在发生错误时打印一条消息。

我试过以下代码:

class MyException(BaseException):
  def __init__(self, msg):
    super(BaseException, self).__init__(msg)
    print "Howdy", msg

__builtins__.Exception = MyException

try:
  raise IOError("world")
except Exception as e:
  pass

我本来期待能看到“Howdy world”这句话被打印出来,但结果什么都没有。

补充说明:

@helmut 建议使用 sys.settrace,这段代码按预期工作。

import sys

def trace(frame, event, arg):
    print event
    return trace

sys.settrace(trace)

def foo():
    raise Exception()

def bar():
    foo()

def baz():
    try:
        bar()
    except:
        pass

baz()

exit()

可惜这对我的情况来说太慢了。

1 个回答

6

为什么不使用猴子补丁?

让我来解释一下这个方法的问题。你把 MyException 赋值给 Exception 的时候,其实是改变了这个模块里的全局变量 Exception。在这个赋值之前定义的所有异常类,或者在其他模块里的异常类,都不会使用这个新的值,它们还是会用原来的那个值。因为 IOError 是在解释器启动时创建的,所以你的赋值对它没有影响。所以如果你试图用猴子补丁的方式去修改 Exception 类,你实际上是在覆盖它的方法。最重要的是,你会改变它的 __init____new__ 方法。可惜的是,这个方法是不被支持的,改变这些属性会导致:

TypeError: can't set attributes of built-in/extension type 'exceptions.Exception'

所以用猴子补丁的方式来修改解释器可能不会成功。

替代方案:追踪

一个替代的方法是写一个函数,然后把它传给 sys.settrace。这个函数会在每次调用其他函数时被调用,如果某个特定的调用需要被追踪,它应该返回另一个追踪函数。各种事件会被传递给这个追踪函数,其中一个事件是 'exception'。通过过滤这些事件,你可能就能达到你想要的效果。

撰写回答