• Python异常处理机制


    总的来说,编写程序时遇到的错误可大致分为 2 类,分别为语法错误和运行时错误。

    >>> print'aaa'
      File "<stdin>", line 1
        print'aaa' 
                 ^
    SyntaxError: invalid syntax         #语法错误
    >>> a = 1/0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ZeroDivisionError: division by zero #运行时错误
    >>> 
    表 1 Python常见异常类型
    异常类型含义实例
    AssertionError 当 assert 关键字后的条件为假时,程序运行会停止并抛出 AssertionError 异常 >>> demo_list = ['C语言中文网']
    >>> assert len(demo_list) > 0
    >>> demo_list.pop()
    'C语言中文网'
    >>> assert len(demo_list) > 0
    Traceback (most recent call last):
      File "<pyshell#6>", line 1, in <module>
        assert len(demo_list) > 0
    AssertionError
    AttributeError 当试图访问的对象属性不存在时抛出的异常 >>> demo_list = ['C语言中文网']
    >>> demo_list.len
    Traceback (most recent call last):
      File "<pyshell#10>", line 1, in <module>
        demo_list.len
    AttributeError: 'list' object has no attribute 'len'
    IndexError 索引超出序列范围会引发此异常 >>> demo_list = ['C语言中文网']
    >>> demo_list[3]
    Traceback (most recent call last):
      File "<pyshell#8>", line 1, in <module>
        demo_list[3]
    IndexError: list index out of range
    KeyError 字典中查找一个不存在的关键字时引发此异常 >>> demo_dict={'C语言中文网':"c.biancheng.net"}
    >>> demo_dict["C语言"]
    Traceback (most recent call last):
      File "<pyshell#12>", line 1, in <module>
        demo_dict["C语言"]
    KeyError: 'C语言'
    NameError 尝试访问一个未声明的变量时,引发此异常 >>> C语言中文网
    Traceback (most recent call last):
      File "<pyshell#15>", line 1, in <module>
        C语言中文网
    NameError: name 'C语言中文网' is not defined
    TypeError 不同类型数据之间的无效操作 >>> 1+'C语言中文网'
    Traceback (most recent call last):
      File "<pyshell#17>", line 1, in <module>
        1+'C语言中文网'
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    ZeroDivisionError 除法运算中除数为 0 引发此异常 >>> a = 1/0
    Traceback (most recent call last):
      File "<pyshell#2>", line 1, in <module>
        a = 1/0
    ZeroDivisionError: division by zero

    提示:表中的异常类型不需要记住,只需简单了解即可。

    Python try except异常处理详解

    Python 提供了try except语句捕获并处理异常,该异常处理语句的基本语法结构如下:
    try:
        可能产生异常的代码块
    except [(Error1, Error2, ...) [as e]]:
        处理异常的代码块1
    except [(Error3, Error4, ...) [as e]]:
        处理异常的代码块2

    try except 语句的执行流程如下:

    1. 首先执行 try 中的代码块,如果执行过程中出现异常,系统会自动生成一个异常对象,该异常对象会提交给 Python 解释器,此过程被称为引发异常。
    2. 当 Python 解释器收到异常对象时,会寻找能处理该异常对象的 except 块,如果找到合适的 except 块,则把该异常对象交给该 except 块处理,这个过程被称为捕获异常。如果 Python 解释器找不到捕获异常的 except 块,则程序运行终止,Python 解释器也将退出。
    [root@kube try]# cat demo.py 
    #coding:utf-8
    
    try:                 #try 代码块用于引发异常
    
        a = int(input('输入被除数:'))
        b = int(input('输入除数:'))
        c = a / b
    except(ValueError, ArithmeticError):    #捕获异常,处理异常
        print('您输入的数值有误,只接受正整数')
    except:                                 #未指定异常类型,表示所有异常
        print('未定义异常')
    
    print(' 继续执行'  )
        
    [root@kube try]# 
    
    [root@kube try]# py demo.py 
    输入被除数:3
    输入除数:3
     继续执行
    [root@kube try]# py demo.py 
    输入被除数:a
    您输入的数值有误,只接受正整数
     继续执行
    [root@kube try]# py demo.py 
    输入被除数:4
    输入除数:0
    您输入的数值有误,只接受正整数
     继续执行

    访问异常信息

    如果程序需要在 except 块中访问异常对象的相关信息,可以通过为 except 块添加as a来实现。当 Python 解释器决定调用某个 except 块来处理该异常对象时,会将异常对象赋值给 except 块后的异常变量,程序即可通过该变量来获得异常对象的相关信息。
    
    所有的异常对象都包含了如下几个常用属性和方法:
    args:该属性返回异常的错误编号和描述字符串。
    errno:该属性返回异常的错误编号。
    strerror:该属性返回异常的描述宇符串。
    with_traceback():通过该方法可处理异常的传播轨迹信息。

    Python try except else详解

     

    [root@kube try]# cat demo3.py 
    s = input('请输入除数:')
    try:
        result = 20 / int(s)
        print('20除以%s的结果是: %g' % (s , result))
    except ValueError:
        print('值错误,您必须输入数值')
    except ArithmeticError:
        print('算术错误,您不能输入0')
    else:
        print('没有出现异常')
    print("程序继续运行")
    [root@kube try]# py demo3.py 
    请输入除数:aa                      #输入字符串时捕获ValueError 然后告警
    值错误,您必须输入数值
    程序继续运行
    [root@kube try]# py demo3.py 
    请输入除数:3               #没有捕获异常程序正常执行,执行 else 代码段
    20除以3的结果是: 6.66667
    没有出现异常
    程序继续运行
    [root@kube try]# 

     

     

    [root@kube try]# cat demo4.py 
    def else_test():
        s = input('请输入除数:')
        result = 20 / int(s)
        print('20除以%s的结果是: %g' % (s , result))
    def right_main():
        try:
            print('try块的代码,没有异常')
        except:
            print('程序出现异常')
        else:                               #当希望程序异常不被 except 捕获时将代码放入 else ,程序的错误就不会被 except 捕获
            # 将else_test放在else块中
            else_test()
    def wrong_main():
        try:
            print('try块的代码,没有异常')
            # 将else_test放在try块代码的后面
            else_test()
        except:
            print('程序出现异常')
    #        else_test()
    wrong_main()
    right_main()
    [root@kube try]# py demo4.py 
    try块的代码,没有异常
    请输入除数:0
    程序出现异常
    try块的代码,没有异常
    请输入除数:0
    Traceback (most recent call last):
      File "demo4.py", line 22, in <module>
        right_main()
      File "demo4.py", line 12, in right_main
        else_test()
      File "demo4.py", line 3, in else_test
        result = 20 / int(s)
    ZeroDivisionError: division by zero
    [root@kube try]# 

     #对比上面两个输出结果,不难发现,放在 else 块中的代码所引发的异常不会被 except 块捕获

     

    Python try except finally:资源回收

    Python 中,finally 语句是与 try 和 except 语句配合使用的,其通常是用来做清理工作的。无论 try 中的语句是否跳入 except 中,最终都要进入 finally 语句,并执行其中的代码块。

    Python 完整的异常处理语法结构如下:
    try:
        #业务实现代码
    except SubException as e:
        #异常处理块1
        ...
    except SubException2 as e:
        #异常处理块2
        ...
    else:
        #正常处理块
    finally :
        #资源回收块
        ...

    在异常处理语法结构中,只有 try 块是必需的,也就是说:
    如果没有 try 块,则不能有后面的 except 块和 finally 块;
    except 块和 finally 块都是可选的,但 except 块和 finally 块至少出现其中之一,也可以同时出现;
    可以有多个 except 块,但捕获父类异常的 except 块应该位于捕获子类异常的 except 块的后面;
    不能只有 try 块,既没有 except 块,也没有 finally 块;
    多个 except 块必须位于 try 块之后,finally 块必须位于所有的 except 块之后。

     finally 语句块和 else 语句块的区别是,else 语句块只有在没有异常发生的情况下才会执行,而 finally 语句则不管异常是否发生都会执行。不仅如此,无论是正常退出、异常退出,还是通过 break、continue、return 语句退出,finally 语句块都会执行。

     

    [root@kube try]# cat demo5.py 
    import os
    def test():
        fis = None
        try:          #执行 try 模块
            fis = open("a.txt")
        except OSError as e:    #如果捕获 OSError ,就赋值给 e ,然后打印指定属性的错误信息
            print(e.strerror)
            # return语句强制方法返回
            return        # ①      #如果错误强制退出
            #os._exit(1)     # ②
        finally:              #最后必须执行 finally   
            # 关闭磁盘文件,回收资源
            if fis is not None:         #判断 fis 是否为None ,None 就跳过,不是就关闭
                try:
                    # 关闭资源
                    fis.close()
                except OSError as ioe:    
                    print(ioe.strerror)
            print("执行finally块里的资源回收!")      
    test()
    [root@kube try]# py demo5.py 
    No such file or directory
    执行finally块里的资源回收!
    [root@kube try]# 

     在通常情况下,不要在 finally 块中使用如 return 或 raise 等导致方法中止的语句(raise 语句将在后面介绍),一旦在 finally 块中使用了 return 或 raise 语句,将会导致 try 块、except 块中的 return、raise 语句失效。看如下程序:

    [root@kube try]# cat demo6.py 
    def test():
        try:
            # 因为finally块中包含了return语句
            # 所以下面的return语句失去作用
            return True
        finally:
            return False
    a = test()
    print(a)
    [root@kube try]# py demo6.py 
    False
    [root@kube try]# 

    Python raise用法

    很多时候,系统是否要引发异常,可能需要根据应用的业务需求来决定,如果程序中的数据、执行与既定的业务需求不符,这就是一种异常。由于与业务需求不符而产生的异常,必须由程序员来决定引发,系统无法引发这种异常。

    如果需要在程序中自行引发异常,则应使用 raise 语句,该语句的基本语法格式为:

    raise [exceptionName [(reason)]]

    其中,用 [] 括起来的为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。

    也就是说,raise 语句有如下三种常用的用法:

      1. raise:单独一个 raise。该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。
      2. raise 异常类名称:raise 后带一个异常类名称。该语句引发指定异常类的默认实例。
      3. raise 异常类名称(描述信息):在引发指定异常的同时,附带异常的描述信息。

    上面三种用法最终都是要引发一个异常实例(即使指定的是异常类,实际上也是引发该类的默认实例),raise 语句每次只能引发一个异常实例。

    [root@kube try]# cat demo7.py 
    
        
    
    
    def main():
        try:
            # 使用try...except来捕捉异常
            # 此时即使程序出现异常,也不会传播给main函数
            mtd(3)
        except Exception as e:
            print('程序出现异常:', e)
        # 不使用try...except捕捉异常,异常会传播出来导致程序中止
        mtd(3)
    def mtd(a):
        if a > 0:
            raise ValueError("a的值大于0,不符合要求")
    main()
    [root@kube try]# py demo7.py 
    程序出现异常: a的值大于0,不符合要求                   #第一次执行 的是 try 里的mtd 由except 捕获异常报出
    Traceback (most recent call last):                #这个是 try 外面的mtd 报错,执行 raise 报错
      File "demo7.py", line 17, in <module>       
        main()
      File "demo7.py", line 13, in main
        mtd(3)
      File "demo7.py", line 16, in mtd
        raise ValueError("a的值大于0,不符合要求")
    ValueError: a的值大于0,不符合要求
    [root@kube try]# 

    except 和 raise 同时使用

    在实际应用中对异常可能需要更复杂的处理方式。当一个异常出现时,单靠某个方法无法完全处理该异常,必须由几个方法协作才可完全处理该异常。也就是说,在异常出现的当前方法中,程序只对异常进行部分处理,还有些处理需要在该方法的调用者中才能完成,所以应该再次引发异常,让该方法的调用者也能捕获到异常。

    为了实现这种通过多个方法协作处理同一个异常的情形,可以在 except 块中结合 raise 语句来完成。如下程序示范了except 和 raise 同时使用的方法:

    [root@kube try]# cat demo10.py 
    class AuctionException(Exception): pass
    class AuctionTest:
        def __init__(self, init_price):
            self.init_price = init_price
        def bid(self, bid_price):
            d = 0.0
            try:
                d = float(bid_price)                 #当 bid 为字符时raise 将异常传递给  except ,标红的将异常传递给 main()
            except Exception as e:
                # 此处只是简单地打印异常信息
                print("转换出异常:", e)
                # 再次引发自定义异常
                raise AuctionException("竞拍价必须是数值,不能包含其他字符!")  #
                raise AuctionException(e)
            if self.init_price > d:
                raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
            initPrice = d
    def main():
        at = AuctionTest(20.4)
        try:
            at.bid("df")
        except AuctionException as ae:
            # 再次捕获到bid()方法中的异常,并对该异常进行处理
            print('main函数捕捉的异常:', ae)
    #a = AuctionTest(100)
    #a.bid(90)
    main()
    
    [root@kube try]# py demo10.py 
    转换出异常: could not convert string to float: 'df'
    main函数捕捉的异常: 竞拍价必须是数值,不能包含其他字符!
    [root@kube try]# 

     这种 except 和 raise 结合使用的情况在实际应用中非常常用。实际应用对异常的处理通常分成两个部分:

    1. 应用后台需要通过日志来记录异常发生的详细情况;
    2. 应用还需要根据异常向应用使用者传达某种提示;


    在这种情形下,所有异常都需要两个方法共同完成,也就必须将 except 和 raise 结合使用。

    如果程序需要将原始异常的详细信息直接传播出去,Python 也允许用自定义异常对原始异常进行包装,只要将上面 ① 号代码改为如下形式即可:

    raise AuctionException(e)

    上面就是把原始异常 e 包装成了 AuctionException 异常,这种方式也被称为异常包装或异常转译。

    自定义异常类

    很多时候,程序可选择引发自定义异常,因为异常的类名通常也包含了该异常的有用信息。所以在引发异常时,应该选择合适的异常类,从而可以明确地描述该异常情况。在这种情形下,应用程序常常需要引发自定义异常。

    用户自定义异常都应该继承 Exception 基类或 Exception 的子类,在自定义异常类时基本不需要书写更多的代码,只要指定自定义异常类的父类即可。

    下面程序创建了一个自定义异常类:

    class AuctionException(Exception):
        pass

    上面程序创建了 AuctionException 异常类,该异常类不需要类体定义,因此使用 pass 语句作为占位符即可。

    在大部分情况下,创建自定义异常类都可采用与上面程序相似的代码来完成,只需改变 AuctionException 异常的类名即可,让该异常的类名可以准确地描述该异常。

     

    Python sys.exc_info()方法:获取异常信息

    在实际调试程序的过程中,有时只获得异常的类型是远远不够的,还需要借助更详细的异常信息才能解决问题。

    捕获异常时,有 2 种方式可获得更多的异常信息,分别是:

    1. 使用 sys 模块中的 exc_info 方法;
    2. 使用 traceback 模块中的相关函数。


    本节首先介绍如何使用 sys 模块中的 exc_info() 方法获得更多的异常信息。

    有关 sys 模块更详细的介绍,可阅读《Python sys模块》。

    模块 sys 中,有两个方法可以返回异常的全部信息,分别是 exc_info() 和 last_traceback(),这两个函数有相同的功能和用法,本节仅以 exc_info() 方法为例。

    exc_info() 方法会将当前的异常信息以元组的形式返回,该元组中包含 3 个元素,分别为 type、value 和 traceback,它们的含义分别是:

      • type:异常类型的名称,它是 BaseException 的子类(有关 Python 异常类,可阅读《Python常见异常类型》一节)
      • value:捕获到的异常实例。
      • traceback:是一个 traceback 对象。
    [root@kube try]# cat demo11.py 
    #coding:utf-8
    
    #使用sys 模块之前,需要使用 import 导入
    
    import sys
    try:
        x = int(input('请输入一个被除数:'))
        print("30除以",x,"等于",30/x)
    except:
        print(sys.exc_info())
        print('其他异常')
    [root@kube try]# py demo11.py 
    请输入一个被除数:0
    (<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x7f8ce2bd8870>)
    其他异常
    [root@kube try]# 

    要查看 traceback 对象包含的内容,需要先引进 traceback 模块,然后调用 traceback 模块中的 print_tb 方法,并将 sys.exc_info() 输出的 traceback 对象作为参数参入。例如:

    [root@kube try]# cat demo12.py 
    #coding:utf-8
    #使用 sys 模块之前,需使用 import 引入
    import sys
    #引入traceback模块
    import traceback
    try:
        x = int(input("请输入一个被除数:"))
        print("30除以",x,"等于",30/x)
    except:
        #print(sys.exc_info())
        traceback.print_tb(sys.exc_info()[2])
        print("其他异常...")
    [root@kube try]# py demo12.py 
    请输入一个被除数:0
      File "demo12.py", line 8, in <module>
        print("30除以",x,"等于",30/x)
    其他异常...
    [root@kube try]# 

     

    Python traceback模块:获取异常信息

    除了使用 sys.exc_info() 方法获取更多的异常信息之外,还可以使用 traceback 模块,该模块可以用来查看异常的传播轨迹,追踪异常触发的源头。

    [root@kube try]# cat demo13.py 
    class SelfException(Exception):
        pass
    def main():
        firstMethod()
    def firstMethod():
        secondMethod()
    def secondMethod():
        thirdMethod()
    def thirdMethod():
        raise SelfException("自定义异常信息")
    main()
    [root@kube try]# py demo13.py 
    Traceback (most recent call last):
      File "demo13.py", line 11, in <module>
        main()
      File "demo13.py", line 4, in main
        firstMethod()
      File "demo13.py", line 6, in firstMethod
        secondMethod()
      File "demo13.py", line 8, in secondMethod
        thirdMethod()
      File "demo13.py", line 10, in thirdMethod
        raise SelfException("自定义异常信息")
    __main__.SelfException: 自定义异常信息
    [root@kube try]# 

    使用 traceback 模块查看异常传播轨迹,首先需要将 traceback 模块引入,该模块提供了如下两个常用方法:

    • traceback.print_exc():将异常传播轨迹信息输出到控制台或指定文件中。
    • format_exc():将异常传播轨迹信息转换成字符串。

     

    [root@kube try]# cat demo14.py 
    # 导入trackback模块
    import traceback
    class SelfException(Exception): pass
    def main():
        firstMethod()
    def firstMethod():
        secondMethod()
    def secondMethod():
        thirdMethod()
    def thirdMethod():
        raise SelfException("自定义异常信息")
    try:
        main()
    except:
        # 捕捉异常,并将异常传播信息输出控制台
        traceback.print_exc()
        # 捕捉异常,并将异常传播信息输出指定文件中
        traceback.print_exc(file=open('log.txt', 'a'))
    [root@kube try]# py demo14.py 
    Traceback (most recent call last):
      File "demo14.py", line 13, in <module>
        main()
      File "demo14.py", line 5, in main
        firstMethod()
      File "demo14.py", line 7, in firstMethod
        secondMethod()
      File "demo14.py", line 9, in secondMethod
        thirdMethod()
      File "demo14.py", line 11, in thirdMethod
        raise SelfException("自定义异常信息")
    SelfException: 自定义异常信息
    [root@kube try]# 

     

     

    Python自定义异常类及用法

    Python 允许用户自定义异常类型。实际开发中,有时候系统提供的异常类型不能满足开发的需求。这时就可以创建一个新的异常类来拥有自己的异常。

    [root@kube try]# cat demo15.py 
    #coding:utf-8
    
    class CustomizeExceptionError(Exception):
        print('捕获异常!!')
        pass
    
    try:
        raise CustomizeExceptionError()
    except CustomizeExceptionError as err:
        print(err)
    [root@kube try]# py demo15.py 
    捕获异常!!
    
    [root@kube try]# 

    [root@kube try]# cat demo16.py 
    class InputError(Exception):
        '''当输出有误时,抛出此异常'''
        #自定义异常类型的初始化
        def __init__(self, value):
            self.value = value
        # 返回异常类对象的说明信息
        def __str__(self):
            return ("{} is invalid input".format(repr(self.value)))
       
    try:
        raise InputError(1) # 抛出 MyInputError 这个异常
    except InputError as err:
        print('error: {}'.format(err))
    [root@kube try]# py demo16.py 
    error: 1 is invalid input
    [root@kube try]# 

     注意,只要自定义的类继承自 Exception,则该类就是一个异常类,至于此类中包含的内容,并没有做任何规定

    Python异常机制使用细则,正确使用Python异常处理机制

    成功的异常处理应该实现如下 4 个目标:

    1. 使程序代码混乱最小化。
    2. 捕获并保留诊断信息。
    3. 通知合适的人员。
    4. 采用合适的方式结束异常活动。

    Python logging模块用法快速攻略

    启用 logging 模块很简单,直接将下面的代码复制到程序开头:
    import logging
    logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    索引
    互联网技术中的算法摘要
    Struts2(六)
    Struts2(五)
    Struts2(四)
    Struts2(三)
    Struts2(二)
    Struts2(一)
    WebService(三)
    WebService(二)
  • 原文地址:https://www.cnblogs.com/zy09/p/11699663.html
Copyright © 2020-2023  润新知