如何在Python中使用自定义消息引发相同的Exception?


145

try我的代码中包含以下代码块:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

严格来说,我实际上是提出了另一个问题 ValueError,而不是ValueError抛出do_something...()err在这种情况下称为。如何将自定义消息附加到err?我尝试下面的代码,但失败,因为err,一个ValueError 实例,不是赎回:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)

13
@Hamish,附加其他信息并重新引发异常在调试时非常有帮助。
约翰·伦德伯格

@Johan绝对是-这就是stacktrace的目的。无法完全理解为什么要编辑现有错误消息而不是引发新错误。
Hamish

@哈米什 可以,但是您可以添加其他内容。对于您的问题,请看一下我的答案和UnicodeDecodeError示例。如果您对此有评论,请改为评论我的答案。
约翰·伦德伯格


1
@Kit是2020年,python 3无处不在。为什么不改变接受的答案奔的答案:-)
麻省理工学院

Answers:


88

更新:对于Python 3,请检查Ben的答案


将消息附加到当前异常并重新引发它:(外部try / except只是为了显示效果)

对于python 2.x,其中x> = 6:

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

这也将做正确的事情,如果err衍生ValueError。例如UnicodeDecodeError

请注意,您可以添加任何内容err。例如err.problematic_array=[1,2,3]


编辑: @Ducan在注释中指出以上内容不适用于python 3,因为.message它不是的成员ValueError。相反,您可以使用此代码(有效的python 2.6或更高版本或3.x):

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

编辑2:

根据目的,您还可以选择在自己的变量名下添加额外的信息。对于python2和python3:

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

9
由于您已经努力使用Python 3样式的异常处理和print,因此您可能应该注意,您的代码在Python 3.x中不起作用,因为message异常没有属性。err.args = (err.args[0] + " hello",) + err.args[1:]可能会更可靠地工作(然后将其转换为字符串以获取消息)。
邓肯

1
不幸的是,不能保证args [0]是表示错误消息的字符串类型-“提供给异常构造函数的参数的元组。某些内置的异常(如IOError)期望有一定数量的参数,并为其赋予特殊含义。该元组的元素,而其他元素通常仅用给出错误信息的单个字符串调用。” 因此,该代码将无法正常工作arg [0]并非错误消息(它可以是int,也可以是表示文件名的字符串)。
特伦特

1
@Taras,有趣。您对此有参考吗?然后,我将添加到一个全新成员:err.my_own_extra_info。或将所有内容封装在我自己的异常中,以保留新信息和原始信息。
约翰·伦德伯格

2
args [0]不是错误消息时的一个真实示例-docs.python.org/2/library/exceptions.html- “ exception EnvironmentError可能在Python系统之外发生的异常的基类:IOError,OSError。当使用2元组创建这种类型的异常时,第一项在实例的errno属性中可用(假定为错误号),第二项在strerror属性中可用(通常是关联的错误消息。元组本身也可以在args属性上使用。”
特伦特

2
我一点都不明白。设置.message属性在此处执行任何操作的唯一原因是显式打印了此属性。如果要在不捕获和打印的情况下引发异常,则不会看到该.message属性做任何有用的事情。
DanielSank

170

如果您有幸仅支持python 3.x,那么这真的很美:)

从...提高

我们可以使用raise from链接异常。

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

在这种情况下,您的呼叫者将捕获的异常具有我们提出异常的地方的行号。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

请注意,底部异常仅包含引发异常的堆栈跟踪。您的调用者仍然可以通过访问__cause__他们捕获的异常的属性来获取原始异常。

with_traceback

或者,您可以使用with_traceback

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

使用此表单,您的调用者将捕获的异常具有追溯到原始错误发生的位置。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

请注意,底部异常包含执行无效除法的行以及重新引发异常的行。


1
是否可以在没有附加回溯的情况下将自定义消息添加到异常中?例如,可以raise Exception('Smelly socks') from e将其修改为仅将“臭袜子”作为对原始回溯的注释,而不是引入自己的新回溯。
joelostblom '16

那就是你从约翰·伦德伯格的回答得到的行为

3
这真的很可爱。谢谢。
allanberry

3
在许多情况下,重新引发新异常或使用新消息引发链异常会造成更多的混乱。异常本身很复杂。更好的策略是,尽可能在err.args + =(“ message”,)中将消息附加到原始异常的参数上,然后重新引发异常消息。追溯可能不会将您带到捕获到异常的行号,但可以肯定地将您带到发生异常的位置。
user-asterix

2
您还可以通过在from子句中指定None 来显式禁止显示异常链:raise RuntimeError("Something bad happened") from None
pfabri

10
try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

印刷品:

There is a problem: invalid literal for int() with base 10: 'a'

1
我想知道是否有Python惯用语,而不是提出另一个实例。
Kit

@Kit-我称之为“重新引发异常”:docs.python.org/reference/simple_stmts.html#raise
eumiro 2012年

1
@eumiro,不,您正在做出新的例外。看我的答案。在您的链接中:“ ...但是,如果要重新引发的异常是当前作用域中最近活动的异常,则应优先使用不带表达式的引发。”
约翰·伦德伯格

3
@JohanLundberg- raise不带参数的将重新引发。如果OP要添加一条消息,则他必须提出一个新的异常,并且可以重新使用原始异常的消息/类型。
eumiro '02

2
如果要添加消息,则不能通过抛出“ ValueError”从头开始创建新消息。这样,您可以销毁它属于哪种ValueError的基础信息(类似于C ++中的切片)。通过重新抛出同样的异常,并提高没有理由,你通过与正确的特定类型(从ValueError异常派生)原来的对象。
约翰·隆德伯格

9

似乎所有答案都将信息添加到e.args [0],从而更改了现有的错误消息。扩展args元组有不利之处吗?我认为可能的好处是,在需要解析该字符串的情况下,您可以不考虑原始错误消息。如果您的自定义错误处理产生了多个消息或错误代码,并且可以通过编程方式(例如通过系统监视工具)解析回溯,则可以在元组中添加多个元素。

## Approach #1, if the exception may not be derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

要么

## Approach #2, if the exception is always derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

您能看到这种方法的缺点吗?


我的旧答案不会更改e.args [0]。
约翰·伦德伯格

4

此代码模板应允许您使用自定义消息引发异常。

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

3
这不会保留堆栈跟踪。
plok

发问者未指定要保留堆栈跟踪。
shrewmouse18年

4

使用以下错误消息引发新异常

raise Exception('your error message')

要么

raise ValueError('your error message')

在您要引发它的地方或使用'from'将错误消息附加(替换)到当前异常中(仅支持Python 3.x):

except ValueError as e:
  raise ValueError('your message') from e

Thanx,@ gberger,“ from e”方法实际上不受python 2.x支持
Alexey Antonenko '19

3

这是我用来在保留原始回溯的同时修改Python 2.7和3.x中的异常消息的功能。这个需要six

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.

    Preserves exception class, and exception traceback.

    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.

    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending

    Returns:
        None

    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

3

Python 3内置异常具有以下strerror字段:

except ValueError as err:
  err.strerror = "New error message"
  raise err

这似乎不起作用。你想念东西吗?
MasayoMusic,

2

当前的答案对我没有用,如果未重新捕获异常,则不会显示附加的消息。

但是,无论是否重新捕获异常,下面的操作都将保留跟踪并显示附加的消息。

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(我使用的是Python 2.7,还没有在Python 3中尝试过)


1

以上解决方案均未达到我想要的目的,即向错误消息的第一部分添加了一些信息,即我希望用户首先看到我的自定义消息。

这为我工作:

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True

if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

0

这仅适用于Python 3。您可以修改异常的原始参数并添加自己的参数。

异常会记住创建它的参数。我认为这是为了您可以修改异常。

在函数中,reraise我们在异常的原始参数之前添加了所需的任何新参数(例如消息)。最后,我们在保留追溯历史的同时重新引发异常。

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''

  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args

  # Recreate the expection and preserve the traceback info so thta we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   


def bad():
  raise ValueError('bad')

def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')

def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')

very_very()

输出

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

-3

如果要自定义错误类型,您可以做的一件简单的事情就是基于ValueError定义一个错误类。


在这种情况下有什么帮助?
约翰·伦德伯格
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.