在 Python 中手动引发(抛出)异常

2024-12-04 08:56:00
admin
原创
137
摘要:问题描述:如何在 Python 中引发异常以便稍后通过块捕获它except?解决方案 1:如何在 Python 中手动抛出/引发异常?使用语义上最适合您的问题的异常构造函数。您的信息应具体,例如:raise ValueError('A very specific bad thing happened.') 不...

问题描述:

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


解决方案 1:

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

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

您的信息应具体,例如:

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

不要引发一般异常

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

问题 1:隐藏错误

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

例如:

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

问题 2:无法捕捉

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

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

最佳实践:raise声明

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

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

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

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

args这些参数可通过对象的属性访问Exception。例如:

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

印刷

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

在 Python 2.5 中,为鼓励用户对 Exceptions 进行子类化并停止使用,message添加了一个实际的属性,但是args的引入和原始的弃用已被撤回。BaseException`args`message

最佳实践:except条款

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

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!

不要修改你的错误……但如果你坚持的话。

您可以使用 来保留堆栈跟踪(和错误值)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] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

如果您愿意,您可以修改新加薪后发生的情况 - 例如args为实例设置新内容:

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

我们在修改参数时保留了整个回溯。请注意,这不是最佳实践,并且它是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>

在Python 3中:

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

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

Python 3,异常链

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

raise RuntimeError('specific message') from error

请注意:

  • 确实允许改变引发的错误类型,并且

  • 这与 Python 2兼容。

已弃用的方法:

这些很容易被隐藏,甚至进入生产代码。你想引发异常,而执行这些操作会引发异常,但不是预期的异常!

下列代码在 Python 2 中有效,但在 Python 3 中无效:

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

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

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

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

示例用法

如果我的 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)))

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

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

您可以创建自己的错误类型,如果您想指出应用程序出现特定错误,只需在异常层次结构中对相应点进行子类化即可:

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

和用法:

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

解决方案 2:

不要这样做。养一只裸犬Exception绝对不是正确的做法;请参阅Aaron Hall 的精彩回答。

没有比这个更 Pythonic 的了:

raise Exception("I know Python!")

替换Exception为您要抛出的异常的具体类型。

如果您想了解更多信息,请参阅Python 的raise 语句文档。

解决方案 3:

在 Python 3 中,有四种不同的引发异常的语法:

  1. 引发异常

  2. 引发异常(参数)

  3. 增加

  4. 从 original_exception 引发异常(参数)

1. 引发异常 vs. 2. 引发异常(参数)

如果您使用raise exception (args)引发异常,那么args在打印异常对象时将会打印 - 如下例所示。

# Raise exception (args)
try:
    raise ValueError("I have raised an Exception")
except ValueError as exp:
    print("Error", exp)     # Output -> Error I have raised an Exception


# Raise exception
try:
    raise ValueError
except ValueError as exp:
    print("Error", exp)     # Output -> Error

3. 声明raise

没有任何参数的语句raise重新引发最后一个异常。

如果您需要在捕获异常后执行某些操作,然后想要重新引发该异常,这很有用。但如果之前没有任何异常,则该raise语句会引发TypeError异常。

def somefunction():
    print("some cleaning")

a = 10
b = 0
result = None

try:
    result = a / b
    print(result)

except Exception:            # Output ->
    somefunction()           # Some cleaning
    raise                    # Traceback (most recent call last):
                             # File "python", line 9, in <module>
                             # ZeroDivisionError: division by zero

4. 从 original_exception 引发异常(args)

该语句用于创建异常链,其中响应另一个异常而引发的异常可以包含原始异常的详细信息 - 如下例所示。

class MyCustomException(Exception):
    pass

a = 10
b = 0
reuslt = None
try:
    try:
        result = a / b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
    print("MyException",exp)
    print(exp.__cause__)

输出:

ZeroDivisionError --  division by zero
MyException Zero Division
division by zero

解决方案 4:

对于常见的情况,你需要抛出异常来响应一些意外的情况,而你永远不打算捕获它,而只是快速失败,以便你在发生这种情况时从那里进行调试 - 最合乎逻辑的似乎是AssertionError

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

解决方案 5:

先阅读现有的答案,这只是一个附录。

请注意,您可以引发带有或不带有参数的异常。

例子:

raise SystemExit

退出程序,但您可能想知道发生了什么。因此您可以使用它。

raise SystemExit("program exited")

这将在关闭程序之前将“程序退出”打印到标准错误。

解决方案 6:

请注意:有时您确实需要处理一般异常。如果您正在处理一堆文件并记录错误,您可能希望捕获文件发生的任何错误,记录它,然后继续处理其余文件。在这种情况下,

try:
    foo()
except Exception as e:
    print(e) # Print out handled error

block 是一个很好的方法。raise不过你仍然需要指定异常,这样你才能知道它们的含义。

解决方案 7:

抛出异常的另一种方法是使用assert。您可以使用assert来验证条件是否得到满足。如果没有,则将引发AssertionError。有关更多详细信息,请参阅此处。

def avg(marks):
    assert len(marks) != 0, "List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:", avg(mark2))

mark1 = []
print("Average of mark1:", avg(mark1))

解决方案 8:

您可能还想引发自定义异常。例如,如果您正在编写一个库,那么为模块创建一个基本异常类,然后使用自定义子异常来更具体,这是一个非常好的做法。

你可以通过如下方式实现:

class MyModuleBaseClass(Exception):
    pass

class MoreSpecificException(MyModuleBaseClass):
    pass


# To raise custom exceptions, you can just
# use the raise keyword
raise MoreSpecificException
raise MoreSpecificException('message')

如果您对自定义基类不感兴趣,您可以从普通异常类(如、、Exception等)继承自定义异常类。TypeError`ValueError`

解决方案 9:

为此,您应该学习Python 的raise语句。

它应该保存在 try 块内。

例子 -

try:
    raise TypeError            # Replace TypeError by any other error if you want
except TypeError:
    print('TypeError raised')

解决方案 10:

如果您不关心引发哪个assert错误,您可以使用引发AssertionError

>>> assert False, "Manually raised error"
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    assert False, "Manually raised error"
AssertionError: Manually raised error
>>> 

如果条件为,则关键字assert会引发错误。在本例中,我们直接指定,因此它会引发错误,但为了让它具有我们希望它引发的文本,我们添加一个逗号并指定我们想要的错误文本。在本例中,我写了,这会用该文本引发它。AssertionError`FalseFalseManually raised error`

解决方案 11:

如果您不关心引发的异常,请执行以下操作:

def crash(): return 0/0

它不允许您向用户发送特定消息,但会导致 Python 解释器崩溃。

对于使用过 Python 的更高级的 Python 用户来说,他们可能会认为表达式将在编译时进行求值(Python 已编译),但 Python 编译器不会在运行时求值表达式。如果我们查看disPython 的 dissasembely 模块显示的内容,我们可以看到字节码。

              2 LOAD_CONST               1 (0)
              4 LOAD_CONST               1 (0)
              6 BINARY_OP               11 (/)

因此本质上 python 会将常数 0 推送到堆栈上,然后运行另一个 0 进行二元运算符进行除法。

虽然这个函数很没用,并且永远不应该在生产代码中使用,但它仍然会导致 Python 解释器崩溃。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用