Python 异常

1. 异常的捕获

2. 异常的传递

3. 自定义异常

4. 异常中抛出异常

 

 

1. 异常的捕获

什么是异常?

当 Python 检测到一个错误时,解释器就无法继续执行了,并且会出现一些错误的提示,这就是所谓的"异常"。

基本语法:

try:
    # 可能产生异常的代码,放在try中
except IOError:  # 需要捕获的异常类型,如IOError
    # 产生异常时的处理方法

示例:捕获多种类型的异常

 1 try:
 2     print('-----test--1---')
 3     open('123.txt', 'r') # 如果 123.txt 文件不存在,那么会产生 IOError 异常
 4     print('-----test--2---')
 5     print(num)  # 如果num变量没有定义,那么会产生 NameError 异常
 6 
 7 # 如果想通过一次except捕获到多个异常可以用一个元组的方式
 8 except (IOError, NameError) as errorMsg:
 9     # errorMsg 里会保存捕获到的错误信息
10     print(errorMsg)

示例:捕获所有类型的异常

 1 try:
 2     print('-----test--1---')
 3     open('123.txt', 'w') # 如果 123.txt 文件不存在,那么会产生 IOError 异常
 4     print('-----test--2---')
 5     print(num)  # 如果num变量没有定义,那么会产生 NameError 异常
 6 
 7 # 捕获所有类型的异常并打印错误信息
 8 except Exception as errorMsg:
 9 # 若不需要打印具体错误信息则可仅使用except:
10 
11     # errorMsg 里会保存捕获到的错误信息
12     print(errorMsg)

else

在 if 中,它的作用是当条件不满足时执行。

同样在 try...except... 中也是如此,即如果没有捕获到异常,那么就执行 else 中的事情。

1 try:
2     num = 100
3     print num
4 except NameError as errorMsg:
5     print('产生错误了:%s' % errorMsg)
6 else:
7     print('没有捕获到异常')

finally

try...finally... 语句用来表达这样的情况:

在程序中,如果有一段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用 finally,比如文件关闭,释放锁,把数据库连接返还给连接池等。

注意:

  • 其实不加finally也不影响程序逻辑,其意义是可读性和完整性。
  • 如果try块中执行到return时并不会直接返回结果,而是先去执行finally块,再返回先前的值,除非finally中有return,finally中的return会破坏程序的完整性且抑制方法产生的异常,不要使用。

示例:

 1 try:
 2     f = open("test.txt")
 3     try:
 4         while 1:
 5             content = f.readline()
 6             if len(content) == 0:
 7                 break
 8             print(content)
 9     except:
10         pass
11     finally:
12         f.close()
13         print("关闭文件")
14 except:
15     print("没有这个文件")

获取具体的错误代码行及错误信息

 1 import traceback
 2 
 3 exception_info = ""
 4 print("start: ")
 5 try:
 6     print(1/0)  # 注释
 7 except:
 8     traceback.print_exc()  # 将具体的错误代码行及错误信息打印到屏幕
 9     exception_info = traceback.format_exc()  #  将具体的错误代码行及错误信息存储到变量中
10 print("end: ")
11 print(exception_info)

执行结果:

start:
Traceback (most recent call last):
  File "code.txt", line 6, in <module>
    print(1/0)  # 注释
ZeroDivisionError: division by zero
end:
Traceback (most recent call last):
  File "code.txt", line 6, in <module>
    print(1/0)  # 注释
ZeroDivisionError: division by zero

 

2. 异常的传递

try 嵌套

在 try 嵌套中,如果里层的 try 没有捕获到这个异常,那么外层的 try 会接收到这个异常,然后进行处理,如果外层的 try 依然没有捕获到,那么再进行传递给外外层,以此类推。

 1 try:
 2     f = open("test.txt")
 3     try:
 4         while 1:
 5             content = f.readline()
 6             if len(content) == 0:
 7                 break
 8             print("文件内容为:{}".format(content))
 9             num
10     finally:
11         f.close()
12         print("关闭文件")
13 except:
14     print("没有这个文件")
15 finally:
16     print("外层的finally")

执行结果:

文件内容为:Hello world.
关闭文件
没有这个文件
外层的finally

函数嵌套调用

  • 如果一个异常是在一个函数中产生的,例如函数A调用函数B,函数B调用函数C,而异常是在函数C中产生的,那么如果函数C中没有对这个异常进行处理,那么这个异常会传递到函数B中,如果函数B有异常处理那么就会按照函数B的处理方式进行执行,如果函数B也没有异常处理,那么这个异常会继续传递,以此类推。如果所有的函数都没有处理,那么此时就会进行异常的默认处理,即报错。
  • 注意观察以下结果,当调用test3函数时,在test1函数内部产生了异常,此异常被传递到test3函数中完成了异常处理,而当异常处理完后,并没有返回到函数test1中进行执行,而是在函数test3中继续执行。
 1 def test1():
 2     print("1-1")
 3     print(num)
 4     print("1-2")
 5     
 6     
 7 # 内部调用函数1,无异常处理机制
 8 def test2():
 9     print("2-1")
10     test1()
11     print("2-2")
12     
13 # 内部调用函数1,有异常处理机制
14 def test3():
15     try:
16         print("3-1")
17         test1()
18         print("3-2")
19     except Exception as msg:
20         print("捕获到了异常,内容为:{}".format(msg))
21     print("3-3")
22     
23     
24     
25 test3()
26 print("-----分割线-----")
27 test2()

执行结果:

3-1
1-1
捕获到了异常,内容为:name 'num' is not defined
3-3
-----分割线-----
2-1
1-1
Traceback (most recent call last):
  File "test.py", line 27, in <module>
    test2()
  File "test.py", line 10, in test2
    test1()
  File "test.py", line 3, in test1
    print(num)
NameError: name 'num' is not defined

 

3. 自定义异常

 可以用raise语句来引发一个自定义的异常。异常/错误对象必须有一个名字,且它们应该是Error或Exception类的子类。

示例:引发自定义异常

 1 class ShortInputException(Exception):
 2     "自定义异常类"
 3 
 4     def __init__(self, length, atlease):
 5         super().__init__()
 6         self.length = length
 7         self.atlease = atlease
 8         
 9 
10 def main():
11     try:
12         s = input("请输入:")
13         if len(s) < 3:
14             # 引发一个自定义异常
15             raise ShortInputException(len(s), 3)
16     # result这个变量被绑定到了错误的实例
17     except ShortInputException as result:
18         print("ShortInputException:输入的长度是%d,至少应是%d" % (result.length, result.atlease))
19     else:
20         print("没有异常")
21         
22 
23 main()

执行结果:

请输入:ab
ShortInputException:输入的长度是2,至少应是3

以上程序中,关于代码 super().__init__() 的说明

  • 这一行代码,可以调用也可以不调用,建议调用,因为__init__()方法往往是用来对创建完的对象进行初始化工作,如果在子类中重写了父类的__init__()方法,意味着父类中的很多初始化工作没有做,这样就不能保证程序的稳定性。
  • 所以在以后的开发中,如果重写了父类的__init__()方法,最好是先调用父类的这个方法,然后再添加自己的功能。

 

4. 异常中抛出异常

 1 class Test:
 2 
 3     def __init__(self, switch):
 4         self.switch = switch  # 是否捕获异常的开关
 5         
 6     def calculate(self, a, b):
 7         try:
 8             return a / b
 9         except Exception as result:
10             # 如果捕获异常的开关为开,则打印捕获信息
11             if self.switch:
12                 print(result)
13             # 否则,抛出异常(抛给外层异常处理机制)
14             else:
15                 raise
16                 
17 
18 t1 = Test(1)  # 打开开关
19 t1.calculate(1, 0)  
20 print("-------分割线--------")
21 t2 = Test(0)  # 关闭开关
22 t2.calculate(1, 0)

执行结果:

division by zero
-------分割线--------
Traceback (most recent call last):
  File "test.py", line 22, in <module>
    t2.calculate(1, 0)
  File "test.py", line 8, in calculate
    return a / b
ZeroDivisionError: division by zero

 

posted @ 2020-02-21 14:10  Juno3550  阅读(191)  评论(0编辑  收藏  举报