在 Python 中手动引发(抛出)异常 [英] Manually raising (throwing) an exception in Python

查看:29
本文介绍了在 Python 中手动引发(抛出)异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在 Python 中引发异常,以便以后可以通过 except 块捕获它?

解决方案

如何在 Python 中手动抛出/引发异常?

使用语义上适合您的问题的最具体的异常构造函数.

在您的消息中具体说明,例如:

raise ValueError('发生了一件非常具体的坏事.')

不要引发通用异常

避免引发通用的Exception.要捕获它,您必须捕获所有其他更具体的异常子类.

问题 1:隐藏错误

raise Exception('I know Python!') # 不要!如果你抓到了,很可能会隐藏虫子.

例如:

def demo_bad_catch():尝试:raise ValueError('代表一个隐藏的错误,不要抓住这个')raise Exception('这是您希望处理的异常')除了异常作为错误:打印('发现这个错误:' + repr(错误))>>>demo_bad_catch()捕捉到这个错误:ValueError('代表一个隐藏的错误,不要捕捉到这个',)

问题 2:抓不到

而且更具体的捕获不会捕获一般异常:

def demo_no_catch():尝试:raise Exception('特定处理未捕获的一般异常')除了 ValueError 作为 e:print('我们不会捕获异常:异常')>>>demo_no_catch()回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中.文件

最佳实践:raise 声明

相反,使用语义上适合您的问题的最具体的异常构造函数.

raise ValueError('发生了一件非常具体的坏事')

它还允许将任意数量的参数传递给构造函数:

raise ValueError('发生了一件非常具体的坏事', 'foo', 'bar', 'baz')

这些参数由 Exception 对象上的 args 属性访问.例如:

尝试:some_code_that_may_raise_our_value_error()除了 ValueError 作为错误:打印(错误.参数)

印刷品

('message', 'foo', 'bar', 'baz')

在 Python 2.5 中,一个实际的 message 属性被添加到 BaseException 以鼓励用户对异常进行子类化并停止使用 args,但是message 的引入和最初的弃用args 已被撤回.

最佳实践:except 子句

当在一个 except 子句中时,例如,您可能想要记录发生了特定类型的错误,然后重新引发.在保留堆栈跟踪的同时执行此操作的最佳方法是使用裸 raise 语句.例如:

logger = logging.getLogger(__name__)尝试:do_something_in_app_that_breaks_easyly()除了 AppError 作为错误:logger.error(错误)提高#就是这个!# raise AppError # 不要这样做,你会丢失堆栈跟踪!

不要修改你的错误......但如果你坚持.

您可以使用 sys.exc_info() 保留堆栈跟踪(和错误值),但是这更容易出错 并且在 Python 之间存在兼容性问题2 和 3,更喜欢使用空的 raise 重新加注.

解释一下 - sys.exc_info() 返回类型、值和回溯.

type, value, traceback = sys.exc_info()

这是 Python 2 中的语法 - 请注意这与 Python 3 不兼容:

raise AppError, error, sys.exc_info()[2] # 避免这种情况.# 等效地,作为错误 * 是 * 第二个对象:提高 sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

如果你愿意,你可以修改你的新加注会发生什么 - 例如为实例设置新的 args:

def error():引发 ValueError('哎呀!')def catch_error_modify_message():尝试:错误()除了值错误:error_type、error_instance、traceback = sys.exc_info()error_instance.args = (error_instance.args[0] + '<修改>',)引发错误类型、错误实例、回溯

我们在修改 args 时保留了整个回溯.请注意,这不是最佳实践,并且在 Python 3 中是无效语法(使得保持兼容性变得更加困难).

<预><代码>>>>catch_error_modify_message()回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中.文件

Python 3 中:

raise error.with_traceback(sys.exc_info()[2])

再次:避免手动操作回溯.它效率较低并且更容易出错.如果您使用线程和 sys.exc_info,您甚至可能会得到错误的回溯(特别是如果您使用异常处理进行控制流 - 我个人倾向于避免这种情况.)>

Python 3,异常链

在 Python 3 中,您可以链接异常,从而保留回溯:

从错误中引发 RuntimeError('specific message')

注意:

  • 确实允许更改引发的错误类型,并且
  • 与 Python 2 兼容.

已弃用的方法:

这些可以轻松隐藏甚至进入生产代码.您想引发异常,而这样做会引发异常,但不是预期的!

在 Python 2 中有效,但在 Python 3 中无效 如下:

raise ValueError, 'message' # 不要这样做,它已被弃用!

只有 有效较旧版本的 Python(2.4 及更低版本),您可能仍会看到有人提出字符串:

raise 'message' # 真的真的错了.不要这样做.

在所有现代版本中,这实际上会引发 TypeError,因为您不会引发 BaseException 类型.如果您没有检查正确的异常并且没有意识到该问题的审阅者,则它可能会进入生产环境.

示例用法

如果我的 API 使用不当,我会提出异常以警告消费者:

def api_func(foo):'''foo 应该是 'baz' 或 'bar'.返回一些非常有用的东西.'''如果 foo 不在 _ALLOWED_ARGS 中:raise ValueError('{foo} 错误,使用baz"或bar"'.format(foo=repr(foo)))

适当时创建自己的错误类型

<块引用>

我想故意犯一个错误,这样它就会进入except"

你可以创建你自己的错误类型,如果你想指出你的应用程序有什么特定的错误,只需在异常层次结构中子类化适当的点:

class MyAppLookupError(LookupError):'''当我的应用程序出现查找错误时提出这个'''

和用法:

如果 important_key 不在 resource_dict 中并且不 ok_to_be_missing:raise MyAppLookupError('资源丢失,那不行.')

How can I raise an exception in Python so that it can later be caught via an except block?

解决方案

How do I manually throw/raise an exception in Python?

Use the most specific Exception constructor that semantically fits your issue.

Be specific in your message, e.g.:

raise ValueError('A very specific bad thing happened.')

Don't raise generic exceptions

Avoid raising a generic Exception. To catch it, you'll have to catch all other more specific exceptions that subclass it.

Problem 1: Hiding bugs

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

For example:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Problem 2: Won't catch

And more specific catches won't catch the general exception:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')
 

>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Best Practices: raise statement

Instead, use the most specific Exception constructor that semantically fits your issue.

raise ValueError('A very specific bad thing happened')

which also handily allows an arbitrary number of arguments to be passed to the constructor:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

These arguments are accessed by the args attribute on the Exception object. For example:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

prints

('message', 'foo', 'bar', 'baz')    

In Python 2.5, an actual message attribute was added to BaseException in favor of encouraging users to subclass Exceptions and stop using args, but the introduction of message and the original deprecation of args has been retracted.

Best Practices: except clause

When inside an except clause, you might want to, for example, log that a specific type of error happened, and then re-raise. The best way to do this while preserving the stack trace is to use a bare raise statement. For example:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Don't modify your errors... but if you insist.

You can preserve the stacktrace (and error value) with sys.exc_info(), but this is way more error prone and has compatibility problems between Python 2 and 3, prefer to use a bare raise to re-raise.

To explain - the sys.exc_info() returns the type, value, and traceback.

type, value, traceback = sys.exc_info()

This is the syntax in Python 2 - note this is not compatible with Python 3:

raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

If you want to, you can modify what happens with your new raise - e.g. setting new args for the instance:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

And we have preserved the whole traceback while modifying the args. Note that this is not a best practice and it is invalid syntax in Python 3 (making keeping compatibility much harder to work around).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

In Python 3:

raise error.with_traceback(sys.exc_info()[2])

Again: avoid manually manipulating tracebacks. It's less efficient and more error prone. And if you're using threading and sys.exc_info you may even get the wrong traceback (especially if you're using exception handling for control flow - which I'd personally tend to avoid.)

Python 3, Exception chaining

In Python 3, you can chain Exceptions, which preserve tracebacks:

raise RuntimeError('specific message') from error

Be aware:

  • this does allow changing the error type raised, and
  • this is not compatible with Python 2.

Deprecated Methods:

These can easily hide and even get into production code. You want to raise an exception, and doing them will raise an exception, but not the one intended!

Valid in Python 2, but not in Python 3 is the following:

raise ValueError, 'message' # Don't do this, it's deprecated!

Only valid in much older versions of Python (2.4 and lower), you may still see people raising strings:

raise 'message' # really really wrong. don't do this.

In all modern versions, this will actually raise a TypeError, because you're not raising a BaseException type. If you're not checking for the right exception and don't have a reviewer that's aware of the issue, it could get into production.

Example Usage

I raise Exceptions to warn consumers of my API if they're using it incorrectly:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Create your own error types when apropos

"I want to make an error on purpose, so that it would go into the except"

You can create your own error types, if you want to indicate something specific is wrong with your application, just subclass the appropriate point in the exception hierarchy:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

and usage:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

这篇关于在 Python 中手动引发(抛出)异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆