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

python: How do I know what type of exception occurred?

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

1
2
3
4
try:
    someFunction()
except:
    print"exception happened!"

但在函数执行过程中,它会引发异常,因此会跳到except部分。

我怎样才能准确地看到导致异常发生的someFunction()中发生的事情?


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

  • 发生错误的事实
  • 发生错误的详细信息(错误隐藏反模式)

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

  • 在GUI中以对话框形式显示异常
  • 将异常从工作线程或进程传输到多线程或多处理应用程序中的控制线程或进程

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

1
2
3
4
5
6
7
try:
    someFunction()
except Exception as ex:
    template ="An exception of type {0} occurred. Arguments:
{1!r}"

    message = template.format(type(ex).__name__, ex.args)
    print message

确保以一种不容错过的方式引起用户的注意。如上图所示,如果消息隐藏在许多其他消息中,那么打印它可能是不够的。不引起用户的注意就等于忽略了所有的例外,如果在阅读完本页上的答案后,你会有一个印象,那就是这不是一件好事。用raise语句结束except块将通过透明地重新发出捕获的异常来解决问题。

上述与仅使用except:而无需任何论证的区别是两方面的:

  • 光秃秃的except:不会给您异常对象来检查
  • 上面的代码并没有捕捉到异常SystemExitKeyboardInterruptGeneratorExit,这通常是您想要的。请参见异常层次结构。

如果您还希望获得相同的stacktrace,如果不捕获异常,则可以这样获得(仍在except子句中):

1
2
import traceback
print traceback.format_exc()

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

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

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

1
2
import pdb
pdb.post_mortem()

我发现这最后一种方法在寻找虫子时非常有用。


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

1
e.__class__.__name__

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

这样地:

1
2
3
4
5
6
7
8
9
10
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!"

您将得到如下输出:

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

经过打印和分析,代码可以决定不处理异常,只执行raise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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"

输出:

1
special case of CustomException not interfering

解释程序打印异常:

1
2
3
4
5
6
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之后,原始异常继续向调用堆栈上传播。(注意可能的陷阱)如果您引发新的异常,它会破坏新的(较短的)堆栈跟踪。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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"

输出:

1
2
3
4
5
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

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


您通常不应该捕获try: ... except的所有可能的异常,因为这太宽泛了。抓住那些不管什么原因都会发生的事情。如果您真的必须这样做,例如,如果您想在调试时了解有关某个问题的更多信息,您应该这样做。

1
2
3
4
5
try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.


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

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

1
2
3
4
5
6
try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

主要的一点是,您不应该捕获一般的异常,而应该只捕获需要捕获的异常。我敢肯定你不想隐藏意想不到的错误或错误。


大多数答案都指向except (…) as (…):语法(没错),但与此同时,没有人想谈论房间里的大象,大象是sys.exc_info()功能。从系统模块文档(Emphasis Mine)中:

This function returns a tuple of three values that give information
about the exception that is currently being handled.
(…)
If no exception is being handled anywhere on the stack, a tuple
containing three None values is returned. Otherwise, the values
returned are (type, value, traceback). Their meaning is: type gets the
type of the exception being handled (a subclass of BaseException);
value gets the exception instance (an instance of the exception type);
traceback gets a traceback object (see the Reference Manual) which
encapsulates the call stack at the point where the exception
originally occurred.

我认为sys.exc_info()可以被看作是对最初的问题的最直接的回答,即我如何知道发生了什么类型的异常?


尝试:一些函数()例外情况除外,不包括:

1
2
3
4
5
6
7
8
9
10
#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))


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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"文件是否被错误地删除。

现在,由于编写异常处理程序不是一个明智的想法(我们还不知道解决它的最佳方法,因为应用程序设计将不断发展),我们只需返回简单的修复程序,即像第一次运行应用程序一样(在本例中)。


您可以按照劳瑞兹的建议开始,使用:

1
except Exception as ex:

然后像这样对print ex说:

1
2
3
4
try:
    #your try code here
except Exception as ex:
    print ex


为了添加到Lauritz的答案中,我创建了一个用于异常处理的修饰器/包装器,以及发生哪种类型的异常的包装器日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:
{1!r}"

            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

这可以用decorator在类方法或独立函数上调用:

@通用函数处理程序

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


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

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

您可以从Python教程了解更多关于异常的信息。


您的问题是:"我如何才能准确地看到导致异常发生的someFunction()中发生的情况?"

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

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


只需避免捕获异常,python打印的回溯将告诉您发生了什么异常。