1、什么是异常
定义:异常就是程序运行时发生错误
构成:追踪信息、错误代码、异常名称、提示信息
2、python程序中常见的异常种类
AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
3、异常处理
python解释器检测到错误,触发异常(也可以是程序员自己触发异常),程序员在程序中编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关),
,如果捕获成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理。
python解释器去执行程序,检测到了一个错误时,触发异常,异常触发后如果是没有被处理的情况下,程序就会在当前异常的地方终止,后面的代码不会再运行。所以为了让我们的
程序能够长期稳定的持续的运行,就必须提供一种异常处理机制来增强你程序的健壮性与容错性,从而能够有效的提升用户体验,维持业务的稳定性
这也就是我们对程序进行异常处理的意义
4、异常处理方式
基础语法
单分支
多分支
万能异常
try...else语句
try...finally语句
抛出异常(主动触发异常)
自定义异常
断言
# 1、使用if判断的方式
# num = input(">>>>>>:")
#
# if num.isdigit():
# int(num) # 这里是程序的主体,其余的都属于异常处理范畴
# elif num.isspace():
# print("您输入的是空格,请重新输入!")
# elif len(num) == 0:
# print("输入为空,请输入正确的数值!")
# else:
# print("不知道哪错了,请您联系客服!")
# 如下不通过逻辑判断进行异常处理会导致程序终止报错
# int(num)
# print("程序执行到了我这里@#@")
# 2、使用特定的语法结构的方式
"""
基本语法:
try:
被检测的代码块
except 异常类型:
try中一旦检测到异常,就执行当前位置的逻辑
单分支:
需要明确的是单分支只能用来处理指定的情况,如果未捕获到异常,则程序依旧会终止报错
try:
order
except NameError as e: # 这里可以使用except与as+变量名的方式搭配使用,打印变量名会直接输出报错信息
print(e) # name 'order' is not defined
多分支:
mul_except = {"广东":["广州", "深圳", "东莞", "惠州"], "江苏":["南京", "苏州", "徐州", "连云港"], "湖北": ["武汉", "宜昌"]}
# try:
# p_name = input(">>>>>>")
# # 打印省级行政单位下辖的市级单位列表
# print(mul_except[p_name])
# c_index = input(">>>>>>")
# # 通过序列下标打印具体某一市级行政单位名称
# print(mul_except[p_name][int(c_index)])
# except ValueError:
# print("列表中没有该省的城市信息")
# except IndexError:
# print("您要获取的元素下标超出序列范围")
# 多分支的方式处理异常使得代码更加的人性化,用户体验会更好
# 万能异常:
在python的异常中,有一个万能异常:Exception,他可以捕获任何异常,但同时它也是一把双刃剑,使用的时候要尽可能谨慎
其使用场景如下:
无论程序出现什么异常,我们都可以丢弃,或者使用同一个逻辑去处理时,一个Exception就足够了
如果是不同的异常类型我们都需要做不同的处理逻辑,那么此时就要用到多分支
在日常的实践当中我们更多的其实是多分支+万能异常这种处理方式,首先通过多分支优先处理一些重要的、已知的异常,一些无关紧
要的、未知的异常则通过万能异常来进行捕获,需要注意的是在联合使用时,万能异常一定要放在最后,否则就没有意义了
# try...else语句
try:
print("此处代码被完整执行")
except IndexError as e:
print(e)
else:
# 该语句通常用来完成一些说明工作
print("try代码块没有触发任何异常,执行此处")
# try...finally语句
def finally_usage():
try:
print("此处代码被完整执行")
print("此处代码触发")
return True
except Exception as e:
print(e)
return False
finally:
# 该语句通常可用来完整一些收尾工作
print("不管try语句中的代码是否报错,都会执行finally分支中的代码")
# 主动触发异常
try:
raise TypeError('类型错误')
except Exception as e:
print(e)
# 自定义异常
当python现有的异常类型无法覆盖我们出现的业务场景时,我们可以自己定义特殊类型的异常,继承Exception类即可
class OrderIdNotExist(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
order_id_list = [13666, 13999, 13888, 13777]
def customize_except(order_id):
if order_id not in order_id_list:
raise OrderIdNotExist("订单ID不存在")
try:
customise_except(333999)
except OrderIdNotExist as e:
print(e)
# 断言
assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达式为假。可以理解assert断言语句为raise-if-not,用来测试表达式
其返回值为假,就会触发异常
assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题,格式如下:
assert expression[, arguments]
assert 表达式[, 参数]
assert 2==7
order_id = 1000000088
order_id_list = [1000000081, 1000000082, 1000000083, 1000000084, 1000000085]
assert order_id in order_id_list
# 异常处理小结:
try:
# 可能发生异常的代码(也就是我们常说的业务代码)
except 异常类型1 as 变量名:
print(变量名) # 变量名存储的是具体的错误信息
except 异常类型2 as 变量名:
print(变量名) # 变量名存储的是具体的错误信息
except Exception as 变量名:
print(变量名) # 变量名存储的是具体的错误信息
else:
print('如果以上代码没有发生异常以及异常处理工作就执行这里的代码')
print('一般情况下else中的代码用来下结论')
# logging模块
finally:
print('不管代码是否有异常都会执行,且在函数中遇到return仍然会执行')
print('一般情况下用于这个函数中资源的回收')
"""
# 基本语法示例
# try:
# order
# except:
# print("程序遇到异常了")
# 单分支示例
# order
# try:
# order
# except NameError as e:
# print("程序遇到变量异常")
# print(e)
#
# print("程序执行到了我这里@#
# 多分支示例
# mul_except = {"广东": ["广州", "深圳", "东莞", "惠州"], "江苏": ["南京", "苏州", "徐州", "连云港"], "湖北": ["武汉", "宜昌"]}
#
# try:
# p_name = input(">>>>>>")
# # 打印省级行政单位下辖的市级单位列表
# print(mul_except[p_name])
# c_index = input(">>>>>>")
# # 通过序列下标打印具体某一市级行政单位名称
# print(mul_except[p_name][int(c_index)])
# except ValueError:
# print("列表中没有该省的城市信息")
# except IndexError:
# print("您要获取的元素下标超出序列范围")
# 万能异常示例(将上一例子添加尾部万能异常代码块,并构造缩进异常即可)
# try...else语句示例
# try:
# print("此处代码被完整执行")
# print("此处代码触发:NameError")
# order
# except IndexError as e:
# print(e)
# else:
# # 该语句通常用来完成一些说明工作
# print("try代码块没有触发任何异常,执行此处")
# try...finally语句示例
# def finally_usage():
# try:
# # print("此处代码被完整执行")
# print("此处代码触发:NameError")
# print(order)
# return True
# except Exception as e:
# print(e)
# return False
# finally:
# # 该语句通常可用来完整一些收尾工作
# print("不管try语句中的代码是否报错,都会执行finally分支中的代码")
#
# finally_usage()
# 抛出异常示例(主动触发异常):
# raise TypeError('类型错误')
# try:
# raise TypeError('类型错误')
# except Exception as e:
# print(e)
# 自定义异常
# 自定义异常,只需自定义异常类继承父类Exception。在自定义异常类中,重写父类init方法
# class OrderIdNotExist(Exception):
# # 自定义异常类型的初始化
# def __init__(self, msg):
# self.msg = msg
#
# # 返回异常类对象的说明信息
# def __str__(self):
# return self.msg
#
#
# def customize_except(order_id):
# order_id_list = [13666, 13999, 13888, 13777]
# if order_id not in order_id_list:
# raise OrderIdNotExist("订单ID不存在")
#
#
# # customize_except(333999)
#
# try:
# customize_except(333999)
# except OrderIdNotExist as e:
# print(e)
# 断言示例
# 格式: assert 条件表达式
# assert 2==7
# order_id = 1000000088
# order_id_list = [1000000081, 1000000082, 1000000083, 1000000084, 1000000085]
# assert order_id in order_id_list
知识点补充
- raise 语句可以自定义报错信息
- raise后的语句是不会被执行了,因为已经抛出异常,控制流将会跳到异常捕捉模块。
- except 语句可以一个except后带多个异常,也可以用多个语句捕捉多个异常,分别做不同处理。
- except语句捕捉的异常如果没有发生,那么except里的语句块是不被执行的。而是执行else里的语句
- 在上面语句中try/except/else/finally所出现的顺序必须是try–>except X–>except–>else–>finally,即所有的except必须在else和finally之前,else(如果有的话)必须在finally之前,而except X必须在except之前。否则会出现语法错误。
6.else和finally都是可选的. - 在上面的完整语句中,else语句的存在必须以except X或者except语句为前提,如果在没有except语句的try block中使用else语句会引发语法错误。
总结:使用特殊格式异常处理相对于使用if逻辑判断处理的异常的优势
try...except这种异常处理机制就是取代if那种逻辑判断方式,从而在不牺牲可读性的前提下增强健壮性和容错性,并且异常处理中为每一个异常定制了异常类型,对于同一种异常,一个except就可以捕捉到,可以同时处理多段代码的异常,减少了代码,增强了可读性