【问题标题】:在 Python 中手动引发(抛出)异常
【发布时间】:2011-01-04 08:59:57
【问题描述】:

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

【问题讨论】:

    标签: python exception


    【解决方案1】:

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

    Use the most specific Exception constructor that semantically fits your issue.

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

    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 声明

    Instead, use the most specific Exception constructor that semantically fits your issue.

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

    它还可以方便地将任意数量的参数传递给构造函数:

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

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

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

    打印

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

    在 Python 2.5 中,BaseException 中添加了一个实际的 message 属性,以鼓励用户将异常子类化并停止使用 args,而是使用 the introduction of message and the original deprecation of args has been retracted

    最佳实践: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])
    

    再次:避免手动操作回溯。这是less efficient,更容易出错。如果你使用线程和sys.exc_info,你甚至可能得到错误的回溯(特别是如果你对控制流使用异常处理——我个人倾向于避免这种情况。)

    Python 3,异常链接

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

    raise RuntimeError('specific message') from error
    

    注意:

    • 确实允许更改引发的错误类型,并且
    • 与 Python 2 兼容。

    不推荐使用的方法:

    这些可以很容易地隐藏甚至进入生产代码。您想引发异常,但这样做会引发异常,但不是预期的!

    Valid in Python 2, but not in Python 3 如下:

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

    valid in much older versions of 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.')
    

    【讨论】:

    • try: raise ValueError('error') except ValueError as e: print('we will catch exception: Exception',str(e)) #是最好的解决方案
    • @SharathBJ 您提出了ValueError 并将其报告为类型Exception,这是不必要的精度损失。 repr(e) 至少会为您报告类型。
    • 官方文档在哪里显示您可以在引发异常时传递消息?例如:raise TypeError("my message")
    • @GabrielStaples 这是关于实例化参数的文档:docs.python.org/3/library/exceptions.html#BaseException.args
    • @AaronHall,啊,我明白了。我没有意识到异常类型,例如TypeError,是对类构造函数的调用!现在有道理了。因此,该消息是错误类型类的通用构造函数 arg。
    【解决方案2】:

    不要这样做。提出一个裸露的Exception 绝对是不是正确的做法;请参阅Aaron Hall's excellent answer

    没有比这更 Pythonic 的了:

    raise Exception("I know python!")
    

    Exception 替换为您要抛出的特定类型的异常。

    如果您想了解更多信息,请参阅the raise statement docs 了解 Python。

    【讨论】:

    • 请不要!这消除了对您捕获的内容进行具体说明的可能性。这完全是错误的做法。看看 Aaron Hall 的出色答案,而不是这个。像这样的时候,我希望每个答案能投不止一个反对票。
    • @PeterR 同样糟糕的是,它的反对票如此之少。对于任何阅读此答案的人,永远不要这样做!正确答案是 Aaron Hall 的答案。
    • 我认为应该有更详细的解释来说明为什么这是错误的或如此糟糕。
    • @CharlieParker 有。这是Aaron Hall's answer的第一部分。
    • 这个答案仍然存在,因为许多有经验的开发人员不同意使用专门的异常类。我希望我能对 cme​​ts 投反对票。
    【解决方案3】:

    在 Python3 中有 4 种不同的语法用于引发异常:

    1. raise exception 
    2. raise exception (args) 
    3. raise
    4. raise exception (args) from original_exception
    

    1. raise exception vs. 2. raise exception (args)

    如果您使用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 execption 
        try:
            raise ValueError
        except ValueError as exp:
            print ("Error", exp)     # Output -> Error 
    

    3.提高

    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 8, 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
    

    【讨论】:

    • 请注意,PEP8 更喜欢 exception(args) 而不是 exception (args)
    • 还有raise exception(args) from None 表示当前活动的异常已被处理,不再感兴趣。否则,如果您在 except 块内引发异常且未处理,则两个异常的回溯将由消息“在处理上述异常期间,发生另一个异常”分开显示
    【解决方案4】:

    对于您需要抛出异常以响应某些意外情况的常见情况,并且您从不打算捕获,而只是快速失败以使您能够从那里进行调试,如果它发生了 - 最合乎逻辑的情况好像是AssertionError:

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

    【讨论】:

    • 这对于ValueError 来说比AssertionError 更好,因为断言没有问题(因为这里没有进行断言)——问题在于值。如果在这种情况下你真的想要AssertionError,请写assert distance &gt; 0, 'Distance must be positive'。但是您不应该以这种方式进行错误检查,因为可以关闭断言 (python -O)。
    • @Two-BitAlchemist 好点。当我编写上面的简单示例时,这个想法在简化中丢失了。在许多类似的情况下,它是与特定值无关的条件。相反,意思是“控制流永远不应该到达这里”。
    • @Two-BitAlchemist 断言可以关闭,是的,但是您根本不应该使用它们进行错误检查?
    • 这取决于。我不会让它成为我打算分发的程序中唯一的错误检查。另一方面,我可以为我的同事制作一个程序,并告诉他们如果使用-O 运行该程序,他们将自担风险。
    • @Two-BitAlchemist 对我来说,断言的作用不是错误检查本身(这是测试的目的),但它们在代码中设置了某些错误无法获得的栅栏通过。因此,追踪和隔离错误变得更加容易,这将不可避免地发生。这只是好习惯不费吹灰之力,而测试则需要大量的努力和大量的时间。
    【解决方案5】:

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

    请注意,您可以使用或不使用参数引发异常。

    例子:

    raise SystemExit
    

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

    raise SystemExit("program exited")
    

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

    【讨论】:

    • 这不是违反 OOP 范式吗?我假设,第一种情况抛出类引用,第二种情况抛出 SystemExit 的实例。 raise SystemExit() 不是更好的选择吗?为什么第一个甚至可以工作?
    【解决方案6】:

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

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

    block 是一个很好的方法。不过,您仍然需要 raise 特定异常,以便了解它们的含义。

    【讨论】:

      【解决方案7】:

      另一种抛出异常的方法是assert。您可以使用 assert 来验证是否满足条件,否则将引发AssertionError。更多详情请查看here

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

      【讨论】:

      • 并非万无一失,因为当使用优化 (-O) 标志调用解释器时,CPython 中的断言会被忽略;如果你想真正控制程序流程“嘿,这种情况不应该发生,但如果它是真的异常结束”,手动raise AssertionError()
      【解决方案8】:

      要捕获所有异常,请使用BaseException,它位于异常层次结构的顶部。

      #1 捕捉异常

      try:
          #Do something
      except BaseException as error:
          print('An exception occurred: {}'.format(error))
      

      #2 引发异常

      try:
          #Do something
      except BaseException as error:
          raise 'An exception occurred: {}'.format(error)
      

      以上代码支持 Python 2.7 到最新版本。

      参考https://docs.python.org/3.9/library/exceptions.html#exception-hierarchy

      【讨论】:

        【解决方案9】:

        你应该为此学习 python 的 raise 语句。 它应该保存在 try 块内。 示例 -

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

        【讨论】:

        • 你能说明你的例子为什么好的原因吗?我已经阅读了这个问题的所有答案,我真的很好奇。
        【解决方案10】:

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

        你可以这样实现:

        class MyModuleBaseClass(Exception):
            pass
        
        class MoreSpecificException(MyModuleBaseClass):
            pass
        
        
        # To raise custom exceptions, you can just
        # use the raise keyword
        raise MoreSpecificException
        raise MoreSpecificException('message')
        

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

        【讨论】:

          【解决方案11】:

          如果您不关心要引发哪个错误,您可以使用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
          >>> 
          

          如果条件是Falseassert 关键字会引发AssertionError,在这种情况下,我们直接指定了False,因此它会引发错误,但要让它有一个我们希望它引发的文本,我们添加一个逗号并指定我们想要的错误文本,在这种情况下我写了Manually raised error,它会用该文本引发它。

          【讨论】:

            【解决方案12】:

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

            def crash(): return 0/0
            

            the good old division be 0

            【讨论】:

            • 搞笑,喜欢。
            猜你喜欢
            • 2014-03-21
            • 2018-03-19
            • 1970-01-01
            • 2020-07-31
            • 2010-09-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多