zoukankan      html  css  js  c++  java
  • python中的异常处理

    python中的异常

    一、异常基础

    在python中,异常会根据错误自动地被触发,也能由代码触发和截获。 异常由四个语句进行处理:

    • try/except: 捕捉由python或你引起的异常,并恢复。

    • try/finally: 无论异常是否发生,执行清理行为。无论是否异常,最后都必须执行finally;

    • try/except/else/finally: 即先执行try,如果有异常,执行except;如果没有异常,执行else;最后,都执行finally;

    • raise: 抛出异常,手动在代码中触发异常。

    • assert: 断言,有条件地在程序员代码中触发异常。主要用于代码调式 或 强制程序满足某个条件,否则报错;

    • with/as: 在python2.6及后续版本中实现环境管理器。我们在文件打开自动关闭那里就用到了这个;

    二、异常的角色

    在python中,异常最常见的几种角色:

    • 错误处理
    • 事件通知
    • 特殊情况处理
    • 终止行为
    • 非常规控制流程

    三、异常处理

    默认异常处理器:打印标准出错消息。这些消息包括引发的异常、堆栈跟踪、用户定义的异常等。

    假如try中,代码块有多个错误,但是,当遇到第一个错误时就中止,直接执行except;故try中的第二个错误就会捕捉不到;

    四、举例

    1、异常处理

    try:
        print('try...')
        r = 10 / 0
        print('result:', r)
    except ZeroDivisionError as e:            # python3中,需要用as
        print('except:', e)
    finally:
        print ('finally...')
    print ('END')
    

    用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。

    如果有多个except,前面匹配到了错误,则后面的except不会捕获错误。例如:

    try:
        print('try...')
        r = 10 / 0
        print('result:', r)
    except BaseException as e:
        print('except1:',e)
    except ZeroDivisionError as e: 
        print('except2:', e)
    finally:
        print ('finally...')
    print ('END')
    

    从上面例子中,可以发现无论什么错误,都是except1捕获。因为Python的错误其实也是class,所有的错误类型都继承自BaseException,任何错误都是BaseException的子集;

    python3.6中异常关系如下:

    BaseException
     +-- SystemExit
     +-- KeyboardInterrupt
     +-- GeneratorExit
     +-- Exception
          +-- StopIteration
          +-- StopAsyncIteration
          +-- ArithmeticError
          |    +-- FloatingPointError
          |    +-- OverflowError
          |    +-- ZeroDivisionError
          +-- AssertionError
          +-- AttributeError
          +-- BufferError
          +-- EOFError
          +-- ImportError
          |    +-- ModuleNotFoundError
          +-- LookupError
          |    +-- IndexError
          |    +-- KeyError
          +-- MemoryError
          +-- NameError
          |    +-- UnboundLocalError
          +-- OSError
          |    +-- BlockingIOError
          |    +-- ChildProcessError
          |    +-- ConnectionError
          |    |    +-- BrokenPipeError
          |    |    +-- ConnectionAbortedError
          |    |    +-- ConnectionRefusedError
          |    |    +-- ConnectionResetError
          |    +-- FileExistsError
          |    +-- FileNotFoundError
          |    +-- InterruptedError
          |    +-- IsADirectoryError
          |    +-- NotADirectoryError
          |    +-- PermissionError
          |    +-- ProcessLookupError
          |    +-- TimeoutError
          +-- ReferenceError
          +-- RuntimeError
          |    +-- NotImplementedError
          |    +-- RecursionError
          +-- SyntaxError
          |    +-- IndentationError
          |         +-- TabError
          +-- SystemError
          +-- TypeError
          +-- ValueError
          |    +-- UnicodeError
          |         +-- UnicodeDecodeError
          |         +-- UnicodeEncodeError
          |         +-- UnicodeTranslateError
          +-- Warning
               +-- DeprecationWarning
               +-- PendingDeprecationWarning
               +-- RuntimeWarning
               +-- SyntaxWarning
               +-- UserWarning
               +-- FutureWarning
               +-- ImportWarning
               +-- UnicodeWarning
               +-- BytesWarning
               +-- ResourceWarning
    

    2、抛出异常

    因为错误是class,捕获一个错误就是捕获到该class的一个实例。因此,错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。

    如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例:

    案例1:自定义异常

    • 自定义异常,print一个对象,实际是两个步骤:1.执行该对象的__str__方法;2.print得到该对象的__str__的返回值,打印

      class FooError(Exception):
          def __init__(self,msg):
              self.message = msg
      
          def __str__(self):
              return self.message
      
      try:
          raise FooError('自定义异常FooError.')
      except FooError as e:
          print(e)
      

    案例2:raise向上级抛异常

    • 原版:

      # err.py
      class FooError(Exception):          #定义错误的class,并指定父类;
          pass
      
      def foo(s):
          n = int(s)
          if n==0:
              raise FooError('invalid value: %s' % s)
          return 10 / n
      

      这时,执行该脚本,会报出异常:

      Traceback (most recent call last): File "/root/test02.py", line 22, in foo(0) File "/root/test02.py", line 19, in foo raise FooError('invalid value: %s' % s) main.FooError: invalid value: 0

      一般我们都尽量使用python自带的异常类型即可。

    • 改进版:

      def foo(s):
          n = int(s)
          return 10 / n
      
      def bar(s):
          try:
              return foo(s)
          except BaseException as e:
              print ('Error!',e)
              raise                       #仅仅捕获错误,不知道应该怎么处理该错误,所以,最恰当的方式是利用raise继续往上抛,让顶层调用者去处理。
      
      def main():
          bar('0')
      
      main()
      

      raise语句如果不带参数,就会把当前错误原样抛出。此外,在except中raise一个Error,还可以把一种类型的错误转化成另一种类型:

      try: 10 / 0 except ZeroDivisionError: raise ValueError('input error!')

      这里将ZeroDivisionError 异常类型转换为了 ValueError。虽然可以转换,但是一定不要转换为毫不相关的异常类型。

    案例3:raise向Exception抛异常

    • 通过raise,抛出指定异常给Exception,可以和Exception一起记录到日志中;

      def db():
          try:
              pass    # 连接数据库,操作
          except Exception as e:
              return False
          else:
              return True
      
      def log(n):
          print(n)
          pass  # 打开文件,记录日志
      
      
      def make():
          try:
              result = db()
              if not result:
                  raise Exception("数据库操作失败.")     # 这里是将错误信息抛给Exception,这样可以通过下面log()记录到日志文件中。
          except Exception as e:
              str_error = str(e)
              log(str_error)
      
      make()
      

    3、代码调试

    我们写代码,都不可能一次搞定。调试时如果使用print语句,那么调试完毕后,代码中到处都是print,我们还得手动删除这些print。

    断言assert

    其实,我们可以用断言代替。凡是用print来辅助查看的地方,都可以用断言(assert)来替代:

    def foo(s):
        n = int(s)
        assert n != 0, 'n is zero!!!'
        return 10 / n
    
    def main():
        foo('0')
    
    main()
    

    assert的意思是,表达式n != 0应该是True,否则,后面的代码就会出错。 如果断言失败,assert语句本身就会抛出AssertionError:

    Traceback (most recent call last):
      ......
        assert n != 0, 'n is zero!!!'
    AssertionError: n is zero!!!                #注意,这里的异常信息是我们断言处自定义的。
    

    程序中如果到处充斥着assert,和print相比也好不到哪去。不过,启动Python解释器时可以用-O参数来关闭assert(把assert当做pass处理):

    # python -O test02.py
    

    logging

    代码调试时把print替换为logging,和assert比,logging不会抛出错误,而且可以输出到文件:

    # err.py
    import logging
    logging.basicConfig(level=logging.DEBUG)
    
    s = '2'
    n = int(s)
    logging.info('n = %d' % n)
    print (10 / n)    
    

    记住,日志很重要!

    pdb

    此外,我们还可以利用启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。这里不再详细讲解这个啦。

    如需转载,请注明出处,否则本人会追究法律责任!
  • 相关阅读:
    影响历史进程的三只苹果
    C语言实现的RSA算法程序(使用GMP)
    C++实现的大整数分解Pollard's rho算法程序
    Java实现的大整数分解Pollard's rho算法程序
    素性测试算法
    素性测试AKS算法程序
    C++实现的Miller-Rabin素性测试程序
    为什么天朝互联网的三巨头是BAT
    KMP模式匹配算法程序(Python,C++,C)
    I00014 汉若塔问题的C++程序
  • 原文地址:https://www.cnblogs.com/hacker001/p/10094775.html
Copyright © 2011-2022 走看看