每当抛出异常时调用钩子函数
假设我想在我的程序中,每当出现任何错误时,都能把这些错误记录到一个文件里。我不想去改动现有的代码。
当然,这个想法可以更广泛地理解为:每当发生错误时,都能插入一个处理的钩子。
那么,下面的代码是否可以安全地实现这个功能呢?
class MyException(Exception):
def my_hook(self):
print('---> my_hook() was called');
def __init__(self, *args, **kwargs):
global BackupException;
self.my_hook();
return BackupException.__init__(self, *args, **kwargs);
def main():
global BackupException;
global Exception;
BackupException = Exception;
Exception = MyException;
raise Exception('Contrived Exception');
if __name__ == '__main__':
main();
4 个回答
这段代码不会影响在main
开始之前创建的任何异常类,而且大多数发生的异常都是这种类型的(比如KeyError
、AttributeError
等等)。而且你实际上无法在最重要的意义上影响这些“内置异常”——如果你的代码中有像1/0这样的操作,真正的ZeroDivisionError会被抛出(这是Python内部处理的),而不是你可能绑定到那个异常名称的其他东西。
所以,我觉得你的代码无法实现你想要的效果(尽管有很多分号,它还是应该是Python,对吧?)——如果想要实现这个功能,基本上需要修改Python运行时的C源代码(例如,提供一个钩子,可以捕捉到任何异常,即使它后来被捕获了)——目前并没有这样的钩子,因为这种用例非常少见(比如,StopIteration
总是在每个for
循环正常结束时被抛出,并且也会被捕获;为什么会有人想要追踪这个,以及Python内部和标准库中许多其他常规使用的捕获异常呢?!)。
根据我所了解,你的代码可能是行不通的。
__init__
方法应该返回 None,但你却想返回一个备份异常的实例。一般来说,如果你想在创建类的实例时改变返回的对象,你应该重写__new__
方法。不幸的是,你不能改变
Exception
类的任何属性。如果可以的话,你可以修改Exception.__new__
,然后在里面放置你的钩子。使用 "
global Exception
" 的方法只对当前模块中的代码有效。Exception
是一个内置类,如果你真的想全局改变它,你需要用import __builtin__; __builtin__.Exception = MyException
。即使你改变了
__builtin__.Exception
,这也只会影响未来对Exception
的使用,已经定义的子类仍然会使用原来的 Exception 类,不会受到你的改变影响。你可以遍历Exception.__subclasses__
,然后为每个子类改变__bases__
,把你的Exception
子类插入进去。有些
Exception
的子类也是内置类型,你也无法修改,虽然我不确定你是否想要修改它们(比如StopIteration
)。
我认为,想要实现你想要的功能,唯一合适的方法就是修改 Python 的源代码。
如果你想记录那些没有被处理的错误,可以使用 sys.excepthook。
我不太明白为什么要记录所有抛出的错误,因为很多库会自己处理一些错误,这些错误可能对你来说并不重要。