在Python中如何获取捕获的异常名称?

230

我如何在Python中获取引发异常的名称?

例如:

try:
    foo = bar
except Exception as exception:
    name_of_exception = ???
    assert name_of_exception == 'NameError'
    print "Failed with exception [%s]" % name_of_exception
例如,我正在捕获多个(或全部)异常,并希望在错误消息中打印异常的名称。

3
你为什么认为需要这样做?为什么不一开始就捕获更具体的异常(例如except NameError:)呢? - user395760
14
我有几种情况需要捕获所有异常(或其中的一些),并希望在错误信息中打印出异常的名称。 - Rob Bednark
2
你可能想要查看标准库的 traceback 模块,它具有一些很好的异常和回溯格式化函数。 - Blckknght
1
@delnan,当您测试函数是否按照编程引发异常时,就会出现这种情况。 - gokul_uf
我需要这样的东西来DRY一些代码:我调用的方法可能会引发几个异常,每个异常都会使用自己的“except”语句处理,但是在每种情况下日志条目非常相似。 - adamc
7个回答

377

以下是几种获取异常类名的方法:

  1. type(exception).__name__
  2. exception.__class__.__name__
  3. exception.__class__.__qualname__

例如:

try:
    foo = bar
except Exception as exception:
    assert type(exception).__name__ == 'NameError'
    assert exception.__class__.__name__ == 'NameError'
    assert exception.__class__.__qualname__ == 'NameError'

2
当您引发 raise socket.timeout 时,您只会获得名称:timeout - Ricky Levi
1
有没有一种方法可以获取 traceback(most recent call last) : ... - user13786942

20
如果您想要完全限定的类名(例如 sqlalchemy.exc.IntegrityError 而不仅仅是 IntegrityError),您可以使用下面的函数。我从另一个问题的MB 的神奇答案中借鉴了这个函数(只是更改了一些变量以适应我的喜好):
def get_full_class_name(obj):
    module = obj.__class__.__module__
    if module is None or module == str.__class__.__module__:
        return obj.__class__.__name__
    return module + '.' + obj.__class__.__name__

例子:

try:
    # <do something with sqlalchemy that angers the database>
except sqlalchemy.exc.SQLAlchemyError as e:
    print(get_full_class_name(e))

# sqlalchemy.exc.IntegrityError

13
您可以使用一些格式化字符串来打印异常:
示例:
try:
    #Code to execute
except Exception as err:
    print(f"{type(err).__name__} was raised: {err}")

它帮了很大的忙。 - Akshat Zala

11

你也可以使用 sys.exc_info()exc_info() 返回三个值:类型、值和回溯信息。 文档链接:https://docs.python.org/3/library/sys.html#sys.exc_info

import sys

try:
    foo = bar
except Exception:
    exc_type, value, traceback = sys.exc_info()
    assert exc_type.__name__ == 'NameError'
    print "Failed with exception [%s]" % exc_type.__name__

6
这个可以用,但似乎有更简单、更直接的方式?
try:
    foo = bar
except Exception as exception:
    assert repr(exception) == '''NameError("name 'bar' is not defined",)'''
    name = repr(exception).split('(')[0]
    assert name == 'NameError'

4
except Exception as exception替换为您想要捕获的异常类型,例如except NameError as exception - Maciej Gol
17
我不想捕获预先知道的特定异常,我希望捕获 所有 异常。 - Rob Bednark

0
当你说“名称”时,这可能意味着几件事情:它可能仅表示“直接”类名,也可能表示完全限定名称。通常后者会更有用且更少出错:我遇到过多种类型具有相同的“直接”类名但来自不同包的情况。
如果您想要打印或记录完全限定名称,则这是最简单的事情之一:
try:
    do_something()
except BaseException as e:
    logger.error(f'whoops! {type(e)}: {e}')
    # or maybe
    print(f'whoops! {type(e)}: {e}', file=sys.stderr)

完全限定的类名将像这样打印出来:“<class 'json.decoder.JSONDecodeError'>”,然后是异常消息。OP说他想要“例如”打印一个消息,因此选择的答案中的assert语句似乎不起作用。 MrName的答案也不是无关紧要的,并且提出了明智的建议! 如果在上面用logger.error(...)替换为logger.exception('whoops!')(NB用户消息是必需的param),则您将记录您的异常的完全限定名称,更不用说它的消息和免费的堆栈跟踪了。
PS 正如名称所示,JSONDecodeError在我的例子中是一个Error,而不是一个Exception(它是ValueError的一个子类)。 捕获BaseException(Error和Exception的超类)通常使得当您没有钓鱼特定异常时生活更轻松。 OP说他想捕获“所有”异常。

-1
这里的其他答案对于探索目的非常好,但如果主要目标是记录异常(包括异常名称),或许考虑使用 logging.exception 而不是 print?

4
你回答了这个问题吗? - Luk Aron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接