每次引发异常时调用一个钩子函数

Age*_*uid 13 python exception

假设我希望每次在我的程序中的任何地方引发任何异常时都能记录到文件.我不想修改任何现有代码.

当然,这可以推广到每次引发异常时都能插入一个钩子.

以下代码是否可以被认为是安全的做这样的事情?

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();
Run Code Online (Sandbox Code Playgroud)

Ken*_*Ken 16

如果要记录未捕获的异常,只需使用sys.excepthook.

我不确定我是否看到了记录所有引发异常的价值,因为很多库会在内部引发/捕获异常,因为你可能不会关心它们.


Cos*_*ean 9

据我所知,你的代码不起作用.

  1. __init__必须返回None,并且您正在尝试返回备份异常的实例.通常,如果您想要更改实例化类时返回的实例,则应该覆盖__new__.

  2. 遗憾的是,您无法更改类中的任何属性Exception.如果这是一个选项,你可以改变Exception.__new__并把钩子放在那里.

  3. " global Exception"技巧仅适用于当前模块中的代码.Exception是一个内置的,如果你真的想要全局改变它,你需要import __builtin__; __builtin__.Exception = MyException

  4. 即使您更改__builtin__.Exception它只会影响将来的使用Exception,已经定义的子类将使用原始的Exception类,并且不受您的更改的影响.您可以循环Exception.__subclasses__并更改其中__bases__的每一个以在Exception那里插入您的子类.

  5. 有些子类Exception也是内置类型,你也无法修改,虽然我不确定你是否想要挂钩它们(想想StopIterration).

我认为唯一可行的方法就是修补Python源代码.


Ale*_*lli 5

此代码不会影响在开始之前创建的任何异常类main,并且发生的大多数异常将是这种类型(KeyError,AttributeError等等).而且你不能真正影响那些最重要意义上的"内置异常" - 如果代码中的任何地方是例如1/0,真正的 ZeroDivisionError将被引发(由Python自己的内部构件),而不是其他任何你可能已经绑定了那个例外的名称.

所以,我不认为你的代码可以做你想要的(尽管所有的分号,它仍然应该是Python,对吗?) - 它可以通过修补Python运行时的C源来实现(例如通过提供一个可能捕获任何异常的钩子,即使它后来被捕获了) - 这样的钩子当前不存在,因为它的用例非常罕见(例如,a StopIteration总是在每个for循环的正常结束时引发-并抓住了;为什么在地球上会想要跟踪,以及Python内部和标准库中捕获异常的许多其他常规用法?!).