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

Kit*_*Kit 123 python message exception

try我的代码中有这个块:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)
Run Code Online (Sandbox Code Playgroud)

严格地说,我实际上是在另一种情况下 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)
Run Code Online (Sandbox Code Playgroud)

Ben*_*Ben 136

我意识到这个问题已经存在了一段时间,但是一旦你足够幸运只支持python 3.x,这真的变得美丽了:)

来自

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

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e
Run Code Online (Sandbox Code Playgroud)

在这种情况下,调用者将捕获的异常具有我们引发异常的地方的行号.

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
Run Code Online (Sandbox Code Playgroud)

注意底部异常只有我们引发异常的堆栈跟踪.您的调用者仍然可以通过访问__cause__他们捕获的异常的属性来获取原始异常.

with_traceback

或者你可以使用with_traceback.

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

请注意,底部异常具有我们执行无效除法的行以及我们重新加载异常的行.

  • **这无法回答实际问题。** 是的,我们都知道如何在 2020 年链接 Python 3.x 异常。实际问题是如何修改原始异常的原始异常消息*不*链接或其他不相关的内容恶作剧只会引发新的异常,从而阻碍最初的回溯。 (12认同)
  • 是否可以在没有额外回溯的情况下向异常添加自定义消息?例如,可以将 `raise Exception('Smelly socks') from e` 修改为只添加“Smelly socks”作为对原始回溯的注释,而不是引入自己的新回溯。 (2认同)
  • 这真的很可爱。谢谢。 (2认同)
  • 在许多情况下,重新引发新的异常或使用新消息引发链式异常会造成更多的混乱。异常本身很难处理。更好的策略是,尽可能在err.args + =(“ message”,)中将消息附加到原始异常的参数上,然后重新引发异常消息。追溯可能不会将您带到捕获到异常的行号,但可以肯定地将您带到发生异常的位置。 (2认同)
  • 您还可以通过在 from 子句中指定 None 来显式抑制异常链的_display_:`raise RuntimeError("Something bad gone") from None` (2认同)

Joh*_*erg 81

更新:对于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)
Run Code Online (Sandbox Code Playgroud)

这也将做正确的事情,如果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))
Run Code Online (Sandbox Code Playgroud)

EDIT2:

根据目的,您还可以选择在自己的变量名称下添加额外信息.对于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
Run Code Online (Sandbox Code Playgroud)

  • 既然你已经开始使用Python 3样式的异常处理和`print`了,你应该注意到你的代码在Python 3.x中不起作用,因为异常上没有`message`属性.`err.args =(err.args [0] +"hello",)+ err.args [1:]`可以更可靠地工作(然后只需转换为字符串来获取消息). (8认同)
  • args [0]不是错误消息的真实示例 - http://docs.python.org/2/library/exceptions.html - "异常EnvironmentError可以在Python系统之外发生的异常的基类: IOError,OSError.当使用2元组创建此类型的异常时,第一个项目在实例的errno属性上可用(假设它是一个错误号),第二个项目在strerror属性上可用(它通常是关联的错误消息).元组本身也可用于args属性." (2认同)
  • 我根本不明白这一点.设置`.message`属性的唯一原因是这个属性是*明确*打印的.如果你在没有捕获和打印的情况下引发异常,你就不会*看到`.message`属性做任何有用的事情. (2认同)

shr*_*use 18

这只适用于 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 exception and preserve the traceback info so that 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()
Run Code Online (Sandbox Code Playgroud)

输出

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')
Run Code Online (Sandbox Code Playgroud)

  • **迄今为止最好的答案。**这是回答原始问题的唯一答案,保留原始回溯,*并且*是纯Python 3.x。疯狂的道具还敲击着“非常非常糟糕”的模因鼓。幽默无疑是一件好事——尤其是在像这样枯燥、技术性的答案中。*太棒了!* (3认同)

wim*_*wim 15

Python 3.11+

\n

PEP 678 \xe2\x80\x93 用注释丰富异常已被接受并登陆 Python 3.11。新的 API 允许用户将自定义消息附加到现有错误。这对于在遇到错误时添加附加上下文很有用。

\n

使用该add_note方法适合回答原问题:

\n
try:\n    int("eleven")\nexcept ValueError as e:\n    errmsg = "My custom error message."\n    e.add_note(errmsg)\n    raise\n
Run Code Online (Sandbox Code Playgroud)\n

它会渲染成这样:

\n
Traceback (most recent call last):\n  File "/tmp/example.py", line 2, in <module>\n    int("eleven")\nValueError: invalid literal for int() with base 10: \'eleven\'\nMy custom error message.\n
Run Code Online (Sandbox Code Playgroud)\n

Python < 3.11

\n

修改args用于BaseException.__str__呈现异常的属性是唯一的方法。您可以扩展参数:

\n
try:\n    int("eleven")\nexcept ValueError as e:\n    errmsg = "My custom error message."\n    e.args += (errmsg,)\n    raise e\n
Run Code Online (Sandbox Code Playgroud)\n

它将呈现为:

\n
Traceback (most recent call last):\n  File "/tmp/example.py", line 2, in <module>\n    int("eleven")\nValueError: ("invalid literal for int() with base 10: \'eleven\'", \'My custom error message.\')\n
Run Code Online (Sandbox Code Playgroud)\n

或者您可以替换args[0],这有点复杂,但会产生更清晰的结果。

\n
try:\n    int("eleven")\nexcept ValueError as e:\n    errmsg = "My custom error message."\n    args = e.args\n    if not args:\n        arg0 = errmsg\n    else:\n        arg0 = f"{args[0]}\\n{errmsg}"\n    e.args = (arg0,) + args[1:]\n    raise\n
Run Code Online (Sandbox Code Playgroud)\n

这将以与 Python 3.11+ 异常相同的方式呈现__notes__

\n
Traceback (most recent call last):\n  File "/tmp/example.py", line 2, in <module>\n    int("eleven")\nValueError: invalid literal for int() with base 10: \'eleven\'\nMy custom error message.\n
Run Code Online (Sandbox Code Playgroud)\n


eum*_*iro 8

try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err
Run Code Online (Sandbox Code Playgroud)

打印:

There is a problem: invalid literal for int() with base 10: 'a'
Run Code Online (Sandbox Code Playgroud)

  • @JohanLundberg - 没有参数的`raise`正在重新加注.如果OP想要添加消息,则必须引发新的异常并重新使用原始异常的消息/类型. (3认同)
  • 我想知道除了提升 _another_ 实例之外,是否有我想要做的事情的 Python 习语。 (2认同)
  • 如果您想*添加*消息,则无法通过抛出"ValueError"从头创建新消息.通过这样做,您可以破坏它是什么类型的ValueError的基础信息(类似于C++中的切片).通过*在没有参数的情况下使用raise重新抛出****异常,您可以使用正确的特定类型(从ValueError派生)传递原始对象. (2认同)

Chr*_*son 7

似乎所有答案都是向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')
Run Code Online (Sandbox Code Playgroud)

要么

## 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')
Run Code Online (Sandbox Code Playgroud)

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


Rug*_*rra 5

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

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")
Run Code Online (Sandbox Code Playgroud)

  • 这不会保留堆栈跟踪。 (5认同)
  • @plok 可以使用 `raise type(err)("my message") from err` (2认同)