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

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

问题描述

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

解决方案


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


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



在您的信息中具体,例如:

  raise ValueError '一个非常具体的坏事发生')



不要引发通用异常:



避免引发一个通用的异常,为了捕捉它,你必须捕获所有其他更具体的例外,将其子类化。



问题1:隐藏错误



  raise异常('我知道Python!')#不要,如果你抓住,隐藏错误。 

例如:

 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $处理')
除了异常作为错误:
print('catch this error:'+ repr(error))

>>> demo_bad_catch()
捕获此错误:ValueError('代表一个隐藏的错误,不要抓住这个')



问题2:不能捕获



,更具体的捕获将不会捕获一般异常:



$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $::::::::::::::::::::::::::::::::::
print('we not catch e')


>>> demo_no_catch()
追溯(最近的最后一次调用):
文件< stdin>,第1行,< module>
文件< stdin>,第3行,在demo_no_catch
异常:一般异常未被特定处理捕获



最佳实践:



raise 语句



而是使用语法上最具体的Exception构造函数适合您的问题

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

这也方便地允许将任意数量的参数传递给构造函数。这在Python 2和3中起作用。

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

这些参数由 args 属性在Exception对象上。例如:

  try:
some_code_that_may_raise_our_value_error()
除了ValueError作为错误:
打印(err.args)

打印

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

在Python 2.5中,将实际的消息属性添加到BaseException中,有利于鼓励用户将异常子类化并停止使用 args ,但引入消息,并且原始弃用的args已被撤回



除了子句



当在except子句中时,您可能希望记录发生特定类型的错误,然后重新提交。保持堆栈跟踪的最佳方法是使用裸露的加注语句,例如:

  logger = logging.getLogger (__name__)

try:
do_something_in_app_that_breaks_easily()
除了AppError作为错误:
logger.error(错误)
raise#just this!
#raise AppError#不要这样做,你会丢失堆栈跟踪!



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



您可以使用 sys.exc_info()保留堆栈跟踪(和错误值),但这种方式更容易出错strong>和在Python 2和3之间存在兼容性问题,更喜欢使用裸机 raise 来重新编译。



要解释 - sys.exc_info()返回类型,值和追溯。

  type,value,traceback = sys.exc_info()

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

 code> raise AppError,error,sys.exc_info()[2]#避免这个。 
#等效的,作为错误*是*第二个对象:
raise sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]

如果你想,你可以修改你的新筹码会发生什么 - 例如为实例设置新的参数:

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

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

我们在修改args时保留了整个追溯。请注意,这不是一个最佳实践,而且在Python 3中这是无效语法(使保持兼容性变得更难解决)。

 >>> catch_error_modify_message()
追溯(最近的最后一次调用):
文件< stdin>,第1行,< module>
文件< stdin>,第3行,在catch_error_modify_message
文件< stdin>,第2行错误
ValueError:oops! <变形>

Python 3

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

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



Python 3,异常链接



在Python 3中,可以链接例外,追踪:

 从错误
中引发RuntimeError('specific message')$ b $ / code>

请注意:




  • 允许更改引发的错误类型,而

  • 这是不兼容Python 2。



已弃用的方法:



这些可以轻松隐藏,甚至可以进入生产代码。你想提出一个例外/错误,并且这样做会产生一个错误,而不是一个错误!



在Python 2中有效但不在Python 3中有效如下:

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

只有在较老版本的Python (2.4及更低版本)中有效,您仍然可以看到提高字符串的人: p>

 提出'消息'#真的很错了。不要这样做

在所有现代版本中,实际上会引发一个TypeError,因为您没有提高BaseException类型。如果您没有检查正确的例外情况,并且没有一个了解该问题的评论者,则可以开始生产。



示例用法:



我提出异常,以警告消费者我的API,如果他们使用不正确:

  def api_func(foo):
'''foo应该是'baz'或'bar'。
如果foo不在_ALLOWED_ARGS中:
raise ValueError('{foo}错误,使用baz或bar'格式(foo = repr(foo)) )



在apropos时创建自己的错误类型:




我想要故意发生错误,因此它将进入


您可以创建自己的错误类型,如果您想要指出应用程序的某些特定错误,只需将异常层次结构中的适当点子类化:



pre> class MyAppLookupError(LookupError):
'''当我的应用程序''

和用法:

 如果important_key不在resource_dict和not ok_to_be_missing:
raise MyAppLookupError('resource is missing,that not not。')


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 e')


>>> 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 Practice:

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. This works in Python 2 and 3.

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.

except clause

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

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 reraise.

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/error, and doing them will raise an error, 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天全站免登陆