温馨提示:本章节是从开发的角度进行学习,如果你是你个非开发的人员学习Python,部分内容可以跳过!!!
一、异常的简解
最初接触异常是在Java中,从目前的发展趋势来看异常机制已经成为判断一门编程语言是否成熟的标准,除了传统的像C语言没有提供异常的处理机制外,主流的编程语言如Java、Kotlin都提供了成熟的异常机制,Python也不例外。
从开发的角度来看:异常处理机制使异常代码和正常的业务逻辑代码分类,保证程序代码更加优雅,并提供了程序的健壮性!
异军突出的语言:Scale、Kotlin、Go、JVM语言的概念
二、为什么需要异常?
原因如下
(1)没有人能保证自己写的程序永远不会出错(即使当前没有出错,版本升级是否兼容呢?)!
(2)即使程序本身没有错误,也无法保证用户按照你的意愿来输入!
(3)即使程序正确,用户输入正确,不能保证运行程序的操作系统的稳定性,更有甚者硬件会不会坏掉,网络畅通不畅通!
插播:运维人员就是(3)的背锅,所以上线后都得"烧香拜佛"!
从开发的角度:尽可能预知所有可能发生的情况,保证程序即使在糟糕的环境中也可以运行!
备注:一般需要测试人员的反馈来提供代码的容错和健壮性!
三、Python异常的关键字
(1)try
try关键字后缩进的代码块简称try块,里面放置的主要是可能引发异常的代码!
(2)except
在except后面对应的是异常类型和和一个代码块,用于表示当发生某种异常,该except块处理这种异常类型的代码块!
备注:except可以有多个,分别匹配不同的异常类型,或者一个except匹配多个异常类型!
(3)else
表明:try代码块中的程序不出现异常时还要执行的else块!
(4)finally
作用:回收try块中打开的物理资源,异常机制保证fiinally块总是被执行(特殊场景除外)
(5)raise
raise用于引发一个实际的异常,raise可以单独作为语句使用,引发一个具体的异常对象!
后续:掌握常见的组合形式!
四、异常处理机制的过程
明确:不管程序代码块是否处于try块中,甚至包括except块中的代码,只要执行该代码出现了异常,系统总是会自动生成一个Error对象,如果这段出现异常的代码定义没有出现任何的except块,该Python解释器无法找到处理该异常的except块,程序就会退出,这个是在学习异常之前常常会遇到的异常退出的情形!
引发异常:执行try块中的代码时出现异常,系统自动生成一个异常对象,该对象提交给Python解释器的过程!
捕获异常:当Python解释器收到异常对象时,会找能处理该异常对象的except块,找到合适的expect块,则把该异常对象交给该expect块处理!
备注:Python解释器找不到捕获异常的expect块,则运行环境终止,Python解释器也将退出!
过程:(1)引发异常--->(2)处理异常吗?
五、异常类的继承体系
(1)Python的异常捕获流程
思考:Python解释器收到异常对象的时候,如何为该异常对象寻找expect块?
过程:Python解释器收到异常对象后,会依次判断该异常对象是否是except块后的异常类或其子类的实例;如果是,Python解释器就调用该except块来处理异常;否则,再次拿该异常对象和下一个except块里的异常类(如果有多个except的化)进行比较!
细节1:正常情况下try块中只有一个except块会被执行,不可能同是有多个except块被执行,只要匹配就退出当前的try体系!
细节2:try块中的一旦某一行代码出现异常,不管异常处理没有,都不会执行try块中的后续代码!
细节3:异常的处理规则--->先处理小异常,再处理大异常!
说明:涉及类的概念和类的继承,后续讲解!
(2)Python中异常类之间的继承关系
五、异常的练习
练习1:使用try..except来捕获异常
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
# 除法
c = a / b
print("输入两个数的相除的结果:", c)
except IndexError:
print('索引异常:程序运行时输入的参数个数不够')
except ValueError:
print('数值错误,是因为程序只接受整数参数')
except ArithmeticError:
print('算术错误')
except ZeroDivisionError:
# ZeroDivisionError是ArithmeticError异常类的子类(except的执行流程)
print('除数不能为0')
except Exception:
print('未知的错误')
说明:要掌握上面的常见异常!
练习2:多异常捕获
import sys
try:
a = int(sys.argv[1])
b = int(sys.argv[2])
# 除法
c = a / b
print("输入两个数的相除的结果:", c)
except (IndexError, ValueError, ArithmeticError): #说明:以元组的形式
print('程序发生列表越界,数字格式异常,算术异常之一都运行该代码')
except:
# 说明:不指定捕获的异常类型,表示会捕获除了上面的任何的异常(比Except更高级)
print('未知异常')
练习3:访问异常对象
"""
所有异常对象的常用属性和方法
(1)args:该属性返回异常的错误编号和描述字符串
"""
try:
c=3/0
except Exception as e:
print(e)
# 说明:二者方式等价-->后面的是放在元组中
print(e.args)
# 自定义一些异常信息
print("自定义的异常信息:",e)
练习4:else块
疑惑:既然当try块没有出现异常时才执行else块,那么直接把else模块放到try块的代码后面不就行了?
def test():
number = input('请输入Number:')
result = 20 / int(number)
print('结果是:', result)
# 注意:test函数放置的位置--->else块
def fun1():
try:
print('try块代码没有异常')
except:
print('程序出现异常')
else:
test()
def fun2():
try:
print('try块代码没有异常')
test()
except:
print('程序出现异常')
#fun1()
fun2()
区别:放在else块中的代码所引发的异常不会被except块捕获!
引申一个常用的场景
(1)希望某段代码的异常能"""被后面的except块捕获""",那么就应该将这段代码放在try块中!
(2)希望某段代码的"""异常向外传播"""(不被except块捕获),那么就将这段代码放在else块中!
练习5:finally
练习省略
特点:finally块中的代码总会被执行(只有使用os._exit(1)或者exit()会直接退出,不会执行finally块中的代码)
细节:尽量不要在finally块中的代码中使用return或raise等导致方法终止的语句,否则会导致try块、expect块中的return、raise语句失效!
场景:将try块中打开了一些物理资源(数据库链接、网络连接、磁盘文件)进行显示回收!
补充:Python的垃圾回收机制不会回收任何的物理资源,只能回收堆内存中对象所占用的内存!
练习6:异常传递
作用机制
"""异常的传递:当(函数/方法)执行出现异常,会将异常传递给(函数/方法)的调用一方!"""
# 如果传递到主程序,仍然没有异常处理,程序才会被终止!
def fun1():
print(8 / 0)
def fun2():
return fun1()
fun2()
报错信息
特点:如果异常不处理,函数中的异常会一级一级的向上(先调用者)传播,最终将该异常传到Python解释器,那么程序就会退出
说明:异常的可以在异常传递过程中的任何一个函数进行捕获!
练习7:raise
"""
需求:提示用户输入密码 如果长度小于8 就抛出异常
"""
def input_passwd():
# 1.提示用户输入密码
pwd = input('请输入密码:')
# 2.判断密码的长度
if len(pwd) >=8:
return pwd
# 3.如果<8就主动抛出异常
else:
print('主动抛出异常')
#a.创建异常对象
ex = Exception('密码长度不够')
#b.主动抛出
raise ex
# 注意:只抛出异常而不捕获异常 代码会出错
try:
print(input_passwd())
except Exception as re:
print(re)
rasise作用:自动引发异常!
开发角度:系统是否要引发异常,可能根据应用的业务需求来决定!