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


229

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

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

但是在执行函数的中间会引发异常,因此它跳到了该except部分。

我如何才能确切地看到someFunction()导致异常发生的原因?


9
永远不使用裸except:(无裸raise),除了也许一旦每个程序,并且优选地不然后。
Mike Graham

如果您使用多个except子句,则无需检查异常类型,这通常是根据特定异常类型采取的相应措施。
里克·波吉

3
如果您关心异常的类型,那是因为您已经考虑了逻辑上可能发生哪种异常类型。
Karl Knechtel 2012年

3
内部except块中的异常可通过sys.exc_info()函数获得- 该函数返回三个值的元组,这些元组提供有关当前正在处理的异常的信息。
Piotr Dobrogost

Answers:


383

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

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

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

那么如何捕获通用异常呢?有几种方法。如果只需要异常对象,请按照以下步骤操作:

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

确保 message被带到用户的注意力在一个难以错过的方式!如上所示,如果将消息掩埋在许多其他消息中,则可能不够用。未能引起用户的注意无异于吞没所有例外,如果您有任何印象,在阅读完本页上的答案后应该会消失,这不是一件好事。在except块末尾添加一个raise语句将通过透明地重新引发捕获的异常来解决该问题。

上面的代码和使用except:不带任何参数的代码之间的区别是双重的:

  • 裸机except:不会给您检查异常对象
  • 上面的代码通常不会捕获这些异常SystemExitKeyboardInterrupt并且GeneratorExit通常是您想要的。请参阅异常层次结构

如果您还希望在不捕获异常的情况下获得相同的堆栈跟踪,则可以这样获取(仍在except子句内):

import traceback
print traceback.format_exc()

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

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

如果您想更深入地研究堆栈,查看变量等,请使用except块内的模块post_mortem功能pdb

import pdb
pdb.post_mortem()

我发现在寻找错误时,这最后一种方法是无价的。


1
我认为traceback.print_exc()与更复杂的“” .join-thing可以做相同的事情。
Gurgeh 2012年

1
@Gurgeh是的,但是我不知道他是否要打印它或将其保存到文件中或对其进行日志记录或对其进行其他操作。
Lauritz V. Thaulow 2012年

我没有投票,但是那是因为您应该在开始时就穿上一件大肥大的衣服,说您不需要任何这些,但是这里是可以做到的。也许是因为您建议捕获通用的Exception。
Rik Poggi 2012年

10
@Rik我想您可能非常需要所有这些。例如,如果您有一个带有GUI和后端的程序,并且希望将后端的所有异常都作为GUI消息呈现,而不是让程序退出堆栈跟踪。在这种情况下,您应该捕获通用的 Exception,为对话框创建回溯文本,也记录该异常,如果处于调试模式,则输入验尸。
Lauritz V. Thaulow 2012年

18
@RikPoggi:天真的想法。在许多合理的情况下,当您需要从别人的代码中捕获异常并且不知道会引发什么异常时。
stackoverflowuser2010

63

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

e.__class__.__name__

并且使用print_exc()函数还将打印堆栈跟踪,这对于任何错误消息都是必不可少的信息。

像这样:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, 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 Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

输出:

special case of CustomException not interfering

解释器输出异常:

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): pass

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

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

输出:

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

请注意,traceback如何不包括calculate()来自9作为原始异常源的line函数e


如果你想存储追溯为一个字符串,你可以使用traceback.format_exc(),以及
Stevoisiak

1
e.__class__.__name__type(e).__name__上面的答案所建议的那样吗?
information_interchange

1
@information_interchange是的。问题和已接受的答案内容随时间完全改变。遗憾的是,SO机械未通知其他参与者:(
亚历克斯

14

通常,您不应捕获所有可能的异常,try: ... except因为这过于广泛。只要抓住由于任何原因而可能发生的事件。如果确实需要,例如,如果您想在调试时查找有关某个问题的更多信息,则应该这样做

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

17
在这里使用“从不”一词从未如此错误。我try: ... except Exception:经常使用很多东西,例如使用依赖网络的库,或者可能会发送奇怪数据的女按摩师。当然,我也有适当的日志记录。这对于在输入数据出现单个错误的情况下允许程序继续运行至关重要。
thnee 2013年

3
是否曾经尝试捕获过使用发送电子邮件时可能引发的所有异常smtplib
linusg

1
在某些特殊情况下,有必要捕获所有异常,但是在一般情况下,您应该仅捕获期望的内容,这样就不会意外隐藏您未曾预料到的错误。当然,良好的日志记录也是一个好主意。
hochl

1
捕获所有异常是完全合理的。如果要调用第三方库,则可能不知道该库中将引发什么异常。在这种情况下,唯一的办法就是捕获所有异常,例如将它们记录在文件中。
stackoverflowuser2010

好的,好的,您是对的,我将重新表述我的答案,以表明存在有效的用例。
hochl

10

除非somefunction是一个非常糟糕的编码遗留函数,否则您不需要所要的内容。

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

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

要点是,您不应捕获一般异常,而应捕获所需的异常。我确定您不想掩盖意外的错误或错误。


8
如果您使用的是第三方库,则可能不知道在库中会引发什么异常。您怎么可能单独捕获所有这些?
stackoverflowuser2010

8

大多数答案都指向except (…) as (…):语法(正确地是这样),但与此同时,没有人愿意谈论房间里的大象,那里的大象是有sys.exc_info()功能的。从文档SYS模块(重点煤矿):

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

我认为sys.exc_info()可以将其视为原始问题“ 我如何知道发生了哪种异常的最直接答案”


1
那是对我的正确答案,因为它确实解决了正在发生什么异常的问题,所以我应该放什么而不是裸露except。仅出于完整性考虑,exctype, value = sys.exc_info()[:2]会告诉您可以在上使用的异常类型except
翁德里·伯克

5

尝试:someFunction()除外,例外:

#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 exc.__class__.__name__已在Alex的答案中提出使用建议– stackoverflow.com/a/9824060/95735
Piotr Dobrogost

3

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


1

这是我处理异常的方式。这样做的想法是尝试解决问题,如果可能的话,然后再添加一个更理想的解决方案。不要在生成异常的代码中解决问题,否则该代码会失去对原始算法的跟踪,应将其写入现场。但是,传递解决问题所需的数据,并返回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”文件被误删除了。

目前,由于编写异常处理程序不是一个聪明的主意(我们尚不知道解决异常的最佳方法,因为应用程序设计会不断发展),因此我们仅返回简单的修复程序,其作用就像我们在运行一样首次使用该应用(在这种情况下)。


0

为了增加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:\n{1!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/


0

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

except Exception as ex:

然后就print ex这样:

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

您能详细说明一下,以便您的答案独立吗?
GHC 2015年

1
确定:您可以像这样打印捕获到的异常:try:#在此处尝试代码,但异常除外,例如ex:print ex现在将打印错误
Gura 2015年


-2

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

在我看来,您不是在问如何在生产代码中处理无法预料的异常(假设有很多答案),而是如何找出导致开发过程中特定异常的原因。

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.