【问题标题】:What's the difference between raise, try, and assert?raise、try 和 assert 有什么区别?
【发布时间】:2021-05-15 21:08:51
【问题描述】:

我学习 Python 已经有一段时间了,raise 函数和 assert 是(我意识到它们都使应用程序崩溃,不像尝试 - 除了)非常相似,我看不到你的情况将使用raiseassert 而不是try

那么,raisetryassert 之间有什么区别?

  • 谢谢tco!不过,我确实有一个问题,如果有人可以向我展示您只能使用其中一个功能的情况。
  • @Defneit python 文档中有这样的例子。此外,您应该在问题中提出您的问题,而不是在评论中。

标签: python assert raise


【解决方案1】:

statement assert 可用于在运行时检查条件,但如果请求优化,则删除来自 Python。扩展形式为:

assert condition, message

相当于:

if __debug__:
    if not condition:
        raise AssertionError(message)

其中__debug__True 是 Python 是不是从选项-O 开始。

所以声明assert condition, message 类似于:

if not condition:
    raise AssertionError(message)

因为两者都提出了AssertionError。不同的是assert condition, message能够通过优化从执行的字节码中删除(当这些被启用时——默认情况下它们不会在 CPython 中应用)。相反,raise AssertionError(message) 在所有情况下都会被执行。

因此,如果代码在任何情况下都应该检查并在检查失败时引发AssertionError,那么编写if not condition: raise AssertionError 是必要的。

【讨论】:

  • 非常感谢 !现在更有意义了!
  • 请注意,优化编译时会删除assert 语句,而不会删除raise AssertionError()
【解决方案2】:

断言:

当您想根据特定条件“停止”脚本并返回一些内容以帮助更快地调试时使用:

list_ = ["a","b","x"]
assert "x" in list_, "x is not in the list"
print("passed") 
#>> prints passed

list_ = ["a","b","c"]
assert "x" in list_, "x is not in the list"
print("passed")
#>> 
Traceback (most recent call last):
  File "python", line 2, in <module>
AssertionError: x is not in the list

增加:

这很有用的两个原因:

1/ 与 try 和 except 块一起使用。引发您选择的错误,可以像下面这样自定义,如果您 passcontinue 脚本,则不会停止脚本;或者可以是预定义的错误raise ValueError()

class Custom_error(BaseException):
    pass

try:
    print("hello")
    raise Custom_error
    print("world")
except Custom_error:
    print("found it not stopping now")

print("im outside")

>> hello
>> found it not stopping now
>> im outside

注意到它没有停止?我们可以在 except 块中使用 exit(1) 来停止它。

2/ Raise 也可用于重新引发当前错误以将其向上传递到堆栈以查看是否有其他东西可以处理它。

except SomeError, e:
     if not can_handle(e):
          raise
     someone_take_care_of_it(e)

尝试/排除块:

完全按照你的想法去做,如果出现错误,你会尝试一些事情,你会抓住它并按照你喜欢的方式处理它。没有例子,因为上面有一个。

【讨论】:

  • 请注意,就像 Ian Goldby 提到的那样,断言在优化时被删除。 assert 语句是实现的内部一致性检查。对于在预期使用代码期间可能发生的错误,请改用raise 错误。
【解决方案3】:

raise - 引发异常。

assert - 引发异常如果给定条件为(或不)为真。

try - 执行一些代码可能引发异常,如果是,则捕获它。

【讨论】:

    【解决方案4】:

    断言

    • 应仅用于调试目的
    • 虽然类似于引发/异常,但它们有不同的用途,因为它们对于指出程序错误无法恢复的情况很有用
    • 断言总是引发 AssertionError 异常,下面是它们的工作方式:

    句法assert_stmt ::= "assert" expression1 ["," expression2]

    在执行时它转换为

    if __debug__:
      if not expression1:
        raise AssertionError(expression2)
    
    • __debug__ 是一个内置标志,通常为 true,但如果触发优化,它将为 false,因此断言将是死代码 => 在启动 Python 时使用 -O 和 -OO 标志禁用(或 PYTHONOPTIMIZE 环境变量CPython),因此,不要依赖它们进行代码逻辑。
    • 由于前一点,请勿使用断言进行数据验证
    • 断言的一个很好的用例 => 如果程序的某些意外状态应使其在所有情况下停止,则使程序“爆炸” => 因此,如果捕获到异常会使程序完全退出的情况下。
    • 如果您有一个没有错误的程序,那么断言将/不应该被触发,它们用作程序的健康检查
    • 在断言中使用数据结构(例如元组)作为表达式1 时要小心,对于非空值总是评估为 True => 断言将始终被触发,从而破坏程序 - 例如:assert (&lt;some_test&gt;, 'warn string') => 注意元组构造(错误!)

    检查:Catching bogus Python asserts on CI by Dan Bader

    引发/例外

    • 它们的目的是处理程序逻辑处于异常状态但您知道要从该状态恢复什么逻辑的情况
    • 当您引发异常时,您可以使异常类型适合错误(更好地控制语义值)并在以后捕获它 => 这样您就可以创建多种您知道如何从中恢复的异常类型,并且处理它们
    • 它们是一种用于处理已知/预期的运行时错误场景的机制
    • 在使用 if 语句和在每个场景中引发验证异常时对数据验证很有用

    尝试

    • 只是编码异常处理的一个句法元素

    顺便说一句,我强烈推荐这本书,"Python Tricks: The Book" Dan Bader(来自realpython.com

    【讨论】:

      【解决方案5】:

      try/except 块可让您捕获和管理异常。异常可以由raiseassert 以及尝试索引空列表等大量错误触发。 raise 通常在您检测到错误情况时使用。 assert 类似,但仅在满足条件时才会引发异常。

      raiseassert 有不同的理念。您检测并引发错误的代码中有许多“正常”错误。可能网站不存在或参数值超出范围。

      断言通常保留用于似乎无论如何都会发生的“我发誓这不会发生”的问题。它更像是运行时调试,而不是正常的运行时错误检测。如果您使用-O 标志或从.pyo 文件而不是.pyc 文件运行,则可以禁用断言,因此它们不应成为常规错误检测的一部分。

      如果生产质量代码引发异常,那么找出你做错了什么。如果它引发AssertionError,那么你的问题就更大了。

      【讨论】:

        【解决方案6】:

        当它们到位时,assertraise AssertionError 之间没有区别,它们将编译为完全相同的字节码:

        import dis
        
        def foo1(param):
            assert param, "fail"
        
        def foo2(param):
            if not param:
                raise AssertionError("fail")
        
        dis.dis(foo1)
        print("*" * 60)
        dis.dis(foo2)
        

        输出:

         4           0 LOAD_FAST                0 (param)
                     2 POP_JUMP_IF_TRUE        12
                     4 LOAD_GLOBAL              0 (AssertionError)
                     6 LOAD_CONST               1 ('fail')
                     8 CALL_FUNCTION            1
                    10 RAISE_VARARGS            1
               >>   12 LOAD_CONST               0 (None)
                    14 RETURN_VALUE
        ************************************************************
         7           0 LOAD_FAST                0 (param)
                     2 POP_JUMP_IF_TRUE        12
        
         8           4 LOAD_GLOBAL              0 (AssertionError)
                     6 LOAD_CONST               1 ('fail')
                     8 CALL_FUNCTION            1
                    10 RAISE_VARARGS            1
               >>   12 LOAD_CONST               0 (None)
                    14 RETURN_VALUE
        

        但请记住,当使用-O-OO 标志运行Python 时,assert 状态将被禁用,任何 raise 语句都不是这种情况。

        【讨论】:

        • 我相信使用 -O 优化器标志时情况并非如此。在那种情况下,raise AssertionError 仍然有效,但 assert 被忽略
        【解决方案7】:

        异常是 Python(和其他一些语言)用来处理执行代码时出现的错误的方法。 raise ExceptionName 表示代码中存在错误,并通过引发与该问题相关的异常来指定它是什么类型的问题。 assert expression 评估 expression 并在它为假时引发异常。

        try 用于执行可能引发您期望的异常的代码。您可以“捕获”异常并在代码中处理它,而不是停止程序。

        示例:假设您有一本字典和一个列表。您想从字典中的列表中查找内容,直到找到不在字典中的内容:

        try:
            for item in my_list:
                print(my_dictionary[item])
        except KeyError as e: #KeyError is the Exception raised when a key is not in a dictionary
            print('There is no {} in the dictionary'.format(e.args[0]))
        

        【讨论】:

        • 非常感谢 !多亏了你,现在我可以继续编码了!
        【解决方案8】:

        Assert 通常用于测试代码以确保某些东西有效:

        def test_bool():
            assert True != False
        

        其中 try, raise 和 except 异常处理是 python 中处理和传播错误的首选方式。

        如果出现问题,大多数库和 python 内置函数都会引发一种或另一种类型的异常。通常在您自己的代码中,当您检测到出现问题时,您还希望引发异常。假设您正在编写一个电子邮件地址验证器,并且如果地址不包含 @ 符号,您想引发异常。你可以有类似的东西(这是玩具代码,实际上不要验证这样的电子邮件):

        def validate_email(address):
            if not "@" in address:
                raise ValueError("Email Addresses must contain @ sign")
        

        然后在代码中的其他地方,您可以调用 validate_email 函数,如果它失败,将抛出异常。

        try:
            validate_email("Mynameisjoe.com")
        except ValueError as ex:
            print("We can do some special invalid input handling here, Like ask the user to retry the input")
        finally:
            close_my_connection()
            print("Finally always runs whether we succeed or not. Good for clean up like shutting things down.")
        

        要知道的重要一点是,当引发异常时,它会向上传递调用堆栈,直到找到处理程序。如果它永远找不到处理程序,那么它将使程序因异常和堆栈跟踪而崩溃。

        您不想做的一件事是:

        if __name__ == '__main__':
            try:
                print(1/0)
            except Exception as ex:
                pass
        

        现在您无法知道为什么您的应用程序崩溃了。

        你会经常看到的一件没问题的事情是:

        import logging
        if __name__ == '__main__':
            try:
                print(1/0)
            except Exception as ex:
                logging.exception(ex)
                raise
        

        在这种情况下,由于没有参数,所以 raise 会重新引发相同的错误。通常在 Web 代码中,您会看到类似的东西不会重新引发异常,因为它会将 500 错误发送到客户端,然后继续下一个请求,因此在这种情况下,您不希望程序结束。

        【讨论】:

          【解决方案9】:

          其他answers 很好地解释了这些差异,但许多人没有提到assert 语句在使用 -O 优化器标志时会被忽略。

          获得与assert 类似的简洁语法的一种选择,在使用 -O 时仍然使异常生效,并获得能够引发特定异常类型的好处是定义一个像这样的实用函数:

          def raiseif(cond, msg="", exc=AssertionError):
              if cond:
                  raise exc(msg)
          
          raiseif(x != y, "x should equal y")
          

          逻辑与assert 相反,但您可以根据需要轻松更改。

          【讨论】:

          • 阅读我上面的答案(stackoverflow.com/a/54252850/359487),您会意识到这实际上已经被提及并解释得很好。
          • 另一种可能性是创建一个名为assert_ 的函数。这个答案中已经提到了倒置的情况——我评论的原因是如何命名新函数。
          【解决方案10】:
          • raise 用于引发异常;
          • assert 用于在给定条件为 False 时引发异常。

          【讨论】:

            猜你喜欢
            • 2017-03-04
            • 1970-01-01
            • 2011-06-15
            • 1970-01-01
            • 2012-06-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-09
            相关资源
            最近更新 更多