python:我怎么知道发生了啥类型的异常?

Posted

技术标签:

【中文标题】python:我怎么知道发生了啥类型的异常?【英文标题】:python: How do I know what type of exception occurred?python:我怎么知道发生了什么类型的异常? 【发布时间】:2012-04-07 02:41:21 【问题描述】:

我有一个被主程序调用的函数:

try:
    someFunction()
except:
    print "exception happened!"

但在函数执行过程中会引发异常,所以会跳转到except部分。

我如何才能确切地看到导致异常发生的 someFunction() 中发生了什么?

【问题讨论】:

永远不要使用裸except:(没有裸raise),除了可能每个程序使用一次,最好不要使用。 如果您使用多个except 子句,则不需要检查异常类型,这通常是根据特定异常类型采取的措施。 如果你关心异常的类型,那是因为你已经考虑了逻辑上可能发生的异常类型。 except 块内,异常可通过 sys.exc_info() 函数获得 - 此函数返回一个包含三个值的元组,这些值提供有关当前正在处理的异常的信息。 相关:How to print an error in Python? 【参考方案1】:

获取异常对象所属类的名称:

e.__class__.__name__

并且使用 print_exc() 函数还将打印堆栈跟踪,这是任何错误消息的基本信息。

像这样:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception as e:
    print ('type is:', e.__class__.__name__)
    print_exc()
    # print("exception happened!")

你会得到这样的输出:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

并且在打印和分析之后,代码可以决定不处理异常,直接执行raise

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except CustomException as e:
    # here do some extra steps in case of CustomException
    print('custom logic doing cleanup and more')
    # then re raise same exception
    raise

输出:

custom logic doing cleanup and more

解释器打印异常:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

raise 之后,原始异常继续向上传播到调用堆栈。 (提防可能的陷阱)如果您提出新的异常,它会导致新的(更短的)堆栈跟踪。

from traceback import print_exc

class CustomException(Exception):
    def __init__(self, ok):
        self.ok = ok

def calculate():
    raise CustomException(False)

try:
    calculate()
except CustomException as e:
    if not e.ok:
        # Always use `raise` to rethrow exception
        # following is usually mistake, but here we want to stress this point
        raise CustomException(e.ok)
    print("handling exception")

输出:

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

注意回溯如何不包括来自9 行的calculate() 函数,这是原始异常e 的起源。

【讨论】:

如果要将traceback存储为字符串,也可以使用traceback.format_exc() e.__class__.__name__type(e).__name__ 一样吗,正如上面的答案所建议的那样? @information_interchange 是的。随着时间的推移,问题和接受的答案内容完全改变了。很遗憾其他参与者没有收到 SO 机器的通知 :( 谢谢,错误链接对我来说是新的!对于向 HTTP 错误注入新消息非常有用。 请直接使用except CustomException,之后不要检查类型。【参考方案2】:

在 Python 2 中,以下是有用的

except Exception, exc:

    # This is how you get the type
    excType = exc.__class__.__name__

    # Here we are printing out information about the Exception
    print 'exception type', excType
    print 'exception msg', str(exc)

    # It's easy to reraise an exception with more information added to it
    msg = 'there was a problem with someFunction'
    raise Exception(msg + 'because of %s: %s' % (excType, exc))

【讨论】:

-1 as using exc.__class__.__name__ 已在 Alex 的回答中提出 – ***.com/a/9824060/95735【参考方案3】:

使用下面的异常类型和异常文本

import sys
print(str(sys.exc_info()[0]).split(' ')[1].strip('>').strip("'")+"-"+(str(sys.exc_info()[1])))

如果你只想要异常类型:使用 -->

import sys
print(str(sys.exc_info()[0]).split(' ')[1].strip('>').strip("'"))
感谢拉杰什瓦尔

【讨论】:

【参考方案4】:

希望这会有所帮助

import sys
varExcepHandling, varExcepHandlingZer = 2, 0
try:
  print(varExcepHandling/varExcepHandlingZer)
except Exception as ex: 
  print(sys.exc_info())

'sys.exc_info()' 将返回一个元组,如果你只想要异常类名使用'sys.exc_info()[0]'

注意:- 如果您想查看所有异常类,只需写 dir(__builtin__)

【讨论】:

【参考方案5】:

这些答案非常适合调试,但对于以编程方式测试异常,isinstance(e, SomeException) 会很方便,因为它也可以测试 SomeException 的子类,因此您可以创建适用于异常层次结构的功能。

【讨论】:

【参考方案6】:

大多数答案指向except (…) as (…): 语法(没错),但同时没有人想谈论房间里的大象,大象是sys.exc_info() 函数。 来自 sys 模块的documentation(重点是我的):

此函数返回一个包含三个值的元组,这些值提供信息 关于当前正在处理的异常。 (…) 如果堆栈上的任何地方都没有处理异常,则一个元组 返回包含三个 None 值。否则,值 返回的是(类型、值、回溯)。它们的含义是:type 获取 正在处理的异常的类型(BaseException 的子类); value 获取异常实例(异常类型的一个实例); traceback 获取一个回溯对象(参见参考手册),该对象 将调用堆栈封装在异常发生的位置 最初发生。

我认为sys.exc_info() 可以被视为对我如何知道发生了什么类型的异常?

的原始问题的最直接答案

【讨论】:

这对我来说是正确的答案,因为它确实解决了发生什么异常的问题,所以我应该放什么而不是裸except。只是为了完整起见,exctype, value = sys.exc_info()[:2] 会告诉您可以在except 上使用的异常类型。【参考方案7】:

其他答案都指出您不应该捕获通用异常,但似乎没有人想告诉您原因,这对于了解何时可以打破“规则”至关重要。 Here 是一个解释。基本上,这样你就不会隐藏:

发生错误的事实 所发生错误的详细信息 (error hiding antipattern)

因此,只要您注意不做任何这些事情,就可以捕获通用异常。例如,您可以通过另一种方式向用户提供有关异常的信息,例如:

在 GUI 中将异常显示为对话框 将异常从工作线程或进程转移到多线程或多处理应用程序中的控制线程或进程

那么如何捕获泛型异常呢?有几种方法。如果您只想要异常对象,请这样做:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type 0 occurred. Arguments:\n1!r"
    message = template.format(type(ex).__name__, ex.args)
    print message

确保message 以一种不容错过的方式引起用户的注意!如果消息隐藏在许多其他消息中,则如上所示打印它可能还不够。未能引起用户注意无异于吞下所有异常,如果您在阅读此页面上的答案后应该有一个印象,那就是这不是一件好事。以 raise 语句结束 except 块将通过透明地重新引发捕获的异常来解决问题。

上面的方法和只使用except: 没有任何参数的区别是双重的:

裸露的except: 不会为您提供要检查的异常对象 上面的代码没有捕捉到异常SystemExitKeyboardInterruptGeneratorExit,这通常是你想要的。请参阅exception hierarchy。

如果您还想要在未捕获异常的情况下获得相同的堆栈跟踪,则可以这样获得(仍在 except 子句中):

import traceback
print traceback.format_exc()

如果您使用logging 模块,您可以将异常打印到日志中(连同一条消息),如下所示:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

如果您想深入挖掘并检查堆栈、查看变量等,请使用 except 块中 pdb 模块的 post_mortem 函数:

import pdb
pdb.post_mortem()

我发现最后一种方法在寻找错误时非常有用。

【讨论】:

我认为 traceback.print_exc() 会和你更复杂的 "".join-thing 做同样的事情。 @Gurgeh 是的,但我不知道他是否想打印它或将其保存到文件或记录它或用它做其他事情。 我没有投反对票,但我想说那是因为你应该在一开始就穿上一件大胖子,说 你不应该需要任何这些,但它是这样的完成。也许是因为您建议捕获通用异常。 @Rik 我认为您可能非常需要all。例如,如果您有一个带有 GUI 和后端的程序,并且您希望将来自后端的所有异常显示为 GUI 消息,而不是让您的程序退出堆栈跟踪。在这种情况下,您应该捕获 generic 异常,为对话框创建回溯文本,同时记录异常,如果处于调试模式,则进入 post-mortem。 @RikPoggi:天真的想法。当你需要从别人的代码中捕获异常并且你不知道会引发什么异常时,有很多合理的情况。【参考方案8】:

以下是我处理异常的方式。如果这很容易,我们的想法是尝试解决问题,然后如果可能,稍后添加更理想的解决方案。不要解决生成异常的代码中的问题,或者该代码丢失了原始算法的跟踪,应该直接写入。但是,传递解决问题所需的数据,并返回一个 lambda,以防您无法在生成它的代码之外解决问题。

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

目前,由于我不想与我的应用程序的目的相切,我没有添加任何复杂的解决方案。但在未来,当我对可能的解决方案有更多了解时(因为应用程序设计得更多),我可以添加以during 为索引的解决方案字典。

在所示示例中,一种解决方案可能是查找存储在其他位置的应用数据,例如“app.p”文件是否被错误删除。

目前,由于编写异常处理程序不是一个聪明的想法(我们还不知道解决它的最佳方法,因为应用程序设计会不断发展),我们只需返回简单的解决方法,就像我们一样'第一次运行应用程序(在这种情况下)。

【讨论】:

【参考方案9】:

您通常不应使用try: ... except 捕获所有可能的异常,因为它过于宽泛。只要抓住那些因任何原因预计会发生的事情。如果你真的必须这样做,例如,如果你想在调试时找出更多关于某个问题的信息,你应该这样做

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

【讨论】:

这里“从不”一词的使用从未如此错误。我在很多事情上都使用try: ... except Exception:,例如使用网络相关库,或者可能会收到奇怪的东西发送给她的数据按摩师。当然,我也有适当的日志记录。这对于允许程序在输入数据出现单一错误的情况下继续运行至关重要。 曾经尝试捕获使用smtplib 发送电子邮件时可能引发的所有异常? 可能有一些特殊情况需要捕获所有异常,但在一般情况下,您应该只捕获您期望的内容,这样您就不会意外隐藏您没有预料到的错误。当然,良好的日志记录也是一个好主意。 捕获所有异常是完全合理的。如果您正在调用第三方库,您可能不知道该库中会引发哪些异常。在这种情况下,唯一的办法是捕获所有异常,例如将它们记录到文件中。 好吧好吧,你是对的,我将重新表述我的答案,以明确说明有有效的用例可以包罗万象。【参考方案10】:

您可以按照 Lauritz 的建议开始:

except Exception as ex:

然后就像这样print ex

try:
    #your try code here
except Exception as ex:
    print ex

【讨论】:

您能否详细说明一下,以便您的答案独立存在? 当然:你可以像这样打印捕获的异常:try: #your try code here except Exception as ex: print ex now the error will be print【参考方案11】:

您的问题是:“我如何才能确切地看到导致异常发生的 someFunction() 中发生了什么?”

在我看来,您不是在问如何在生产代码中处理不可预见的异常(正如许多答案所假设的那样),而是在开发过程中如何找出导致特定异常的原因。

最简单的方法是使用一个调试器,它可以在发生未捕获异常的地方停止,最好不要退出,这样您就可以检查变量。例如,Eclipse 开源 IDE 中的 PyDev 可以做到这一点。要在 Eclipse 中启用它,请打开 Debug 透视图,在 Run 菜单中选择 Manage Python Exception Breakpoints,然后选中 Suspend on uncaught exceptions

【讨论】:

【参考方案12】:

为了补充 Lauritz 的回答,我为异常处理创建了一个装饰器/包装器,并且包装器记录了发生的异常类型。

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type 0 occured. Arguments:\n1!r"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

这可以在类方法或带有装饰器的独立函数上调用:

@general_function_handler

有关完整示例,请参阅我的博客:http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/

【讨论】:

【参考方案13】:

除非somefunction 是一个非常糟糕的编码遗留函数,否则你不应该需要你所要求的。

使用多个except子句以不同的方式处理不同的异常:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

重点是您不应该捕获一般异常,而应该只捕获您需要的异常。我确定您不想隐藏意外错误或错误。

【讨论】:

如果您使用的是第三方库,您可能不知道其中会引发哪些异常。你怎么可能单独抓到它们?【参考方案14】:

不要捕获异常,Python 打印的回溯会告诉你发生了什么异常。

【讨论】:

【参考方案15】:

可以通过以下方式捕获实际异常:

try:
    i = 1/0
except Exception as e:
    print e

您可以从The Python Tutorial 了解有关异常的更多信息。

【讨论】:

以上是关于python:我怎么知道发生了啥类型的异常?的主要内容,如果未能解决你的问题,请参考以下文章

14python异常处理及断言

我怎么知道引发异常的确切命令?

抛出“密钥不存在”异常。我究竟做错了啥?

我的 php artisan 迁移发生了啥

异常处理

Python异常处理