【问题标题】:What's the difference between raise, try, and assert?raise、try 和 assert 之间有什么区别?
【发布时间】:2017-03-04 03:17:02
【问题描述】:

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

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

【问题讨论】:

  • 感谢 tcoc!不过,我确实有一个问题,如果有人可以向我展示您只能使用其中一个功能的情况。
  • @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】:

raise - 引发异常。

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

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

【讨论】:

    【解决方案3】:

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

    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]))
    

    【讨论】:

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

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

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

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

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

    【讨论】:

      【解决方案5】:

      断言:

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

      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 错误。
      【解决方案6】:

      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
      

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

      【讨论】:

        【解决方案7】:

        断言

        • 应仅用于调试目的
        • 虽然与引发/异常类似,但它们的用途不同,因为它们对于指出程序错误无法恢复的场景很有用
        • 断言总是引发 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

        【讨论】:

          【解决方案8】:

          当它们到位时,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 被忽略
          【解决方案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 时引发异常。

          【讨论】:

            猜你喜欢
            • 2021-05-15
            • 1970-01-01
            • 2011-06-15
            • 1970-01-01
            • 2012-06-17
            • 1970-01-01
            • 2013-10-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多