Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
388 views
in Technique[技术] by (71.8m points)

python - 在Python中手动引发(抛出)异常(Manually raising (throwing) an exception in Python)

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

  ask by TIMEX translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

How do I manually throw/raise an exception in Python? (如何在Python中手动引发/引发异常?)

Use the most specific Exception constructor that semantically fits your issue . (使用在语义上适合您的问题的最特定的Exception构造函数 。)

Be specific in your message, eg: (在您的消息中要具体,例如:)

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 (问题1:隐藏错误)

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 (问题2:无法抓住)

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 (最佳做法: raise声明)

Instead, use the most specific Exception constructor that semantically fits your issue . (而是使用在语义上适合您的issue的最特定的Exception构造函数 。)

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. (这些参数由Exception对象上的args属性访问。) 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 . (在Python 2.5中,实际的message属性已添加到BaseException中,以鼓励用户继承Exceptions的子类并停止使用args ,但是message的引入和args的原始弃用已被收回 。)

Best Practices: except clause (最佳做法: except子句)

When inside an except clause, you might want to, for example, log that a specific type of error happened, and then re-raise. (例如,在except子句中时,您可能想要记录发生了特定类型的错误,然后重新引发。) 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. (您可以使用sys.exc_info()来保留stacktrace(和错误值),但这是更容易出错的方式,并且在Python 2和3之间存在兼容性问题 ,建议您使用裸raise来重新筹集资金。)

To explain - the sys.exc_info() returns the type, value, and traceback. (解释一下sys.exc_info()返回类型,值和回溯。)

type, value, traceback = sys.exc_info()

This is the syntax in Python 2 - note this is not compatible with Python 3: (这是Python 2中的语法-请注意,这与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 - eg 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. (并且我们在修改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). (请注意,这不是最佳做法,并且在Python 3中是无效的语法 (使得保持兼容性变得更加困难)。)

>>> 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 : (在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.) (而且,如果您使用线程和sys.exc_info您甚至可能会得到错误的回溯(尤其是如果您对控制流使用异常处理,我个人倾向于避免这种情况。))

Python 3, Exception chaining (Python 3,异常链接)

In Python 3, you can chain Exceptions, which preserve tracebacks: (在Python 3中,您可以链接异常,以保留回溯:)

    raise RuntimeError('specific message') from error

Be aware: (意识到:)

  • this does allow changing the error type raised, and (这确实允许更改引发的错误类型,并且)
  • this is not compatible with Python 2. (这与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: (在Python 2中有效,但在Python 3中无效 :)

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: (仅在更旧的Python版本 (2.4及更低版本 )中有效,您仍然可以看到有人在引发字符串:)

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. (在所有现代版本中,这实际上会引发TypeError,因为您没有引发BaseException类型。) 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: (我提出异常以警告使用者如果我的API使用不正确:)

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" (


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...