导引问题
1. 在实际工作中,我们一道的情况不可能是非常完美的.
(1) 你写的某个模块,用户输入不一定符合你的要求
(2) 程序要求打开某个文件,但是文件可能不存在或文件的格式不对
(3) 你要读取数据库的数据,但是数据可能为空
(4) 程序运行着,但是磁盘或者内存满了
2. 软件程序在运行过程中,非常可能遇到刚刚提到的这些问题,我们称这些可能发生的错误为异常(Exception),意思是例外.
3. 遇到这种例外情况,或者叫异常,我们怎么让写的仍需做出合理的处理,而不至于程序崩溃呢?这就是本章要学习的知识.
异常机制本质
1. 异常指程序运行过程中出现的非正常现象,例如用户输入错误,除数为零,需要处理的文本不存在,数组下标越界等.
2. 所谓异常处理,就是指程序在出现问题时依然可以正确的执行剩余的程序,而不会因为异常而终止程序执行.
3. Python中,引进了很多用来描述和处理异常的类,称为异常类. 异常类定义中包含了该类异常的信息和对异常进行处理方法.
异常解决的关键: 定位
当异常发生时,解释器会报相关的错误信息,并会在控制台打印出相关错误信息.
我们只需暗中从上到下的顺序即可追溯(trackback)错误发生的过程,最终定位因错误的哪一行代码即可
try...一个except结构
1. try...except 是最常见的异常处理结构.
2. 语法格式:
try:
被监控的可能引发异常的语句块
except BaseException [as e]:
异常处理语句块
(1) try中包含着可能引发异常的代码,except块则用来捕捉和处理发生的异常.
(2) 执行的时候,如果try块中没有引发异常,则跳过except块继续执行后续的代码.
(3) 执行的时候,如果try块中发生了异常,则跳过try块中的后续代码,跳到相应的except块中处理异常,异常处理完后,直接执行 try...except 后续的代码.
# code02_try_except_01.py
def main():
print("start!")
try:
print("step1")
a = 3 / 0 # 除数为0异常
print("step2") # 设置步骤2是要说明,如果有异常就不会往下执行 (start -> 1 -> 3 -> end)
except Exception as e: # except BaseException/Exception as e / except:
print("step3")
print(type(e), e) # 查看e这个异常类的 类型 和 信息
print("end!")
if __name__ == "__main__":
main()
# code03_try_except_02.py
# 循环输入数字,如果不是数字则处理异常,直到输入88循环结束
def main():
while True:
try:
# 如果输入不是整数的话,会有 ValueError
x = int(input("请输入一个数字(88结束循环):").strip())
if x == 88: break
except Exception as e:
print(type(e), e,"输入不是数字,请重新输入")
print("循环数字输入结束!")
if __name__ == "__main__":
main()
try...多个except结构
为了严谨,如果很多人调用你的项目代码,那么异常处理就应该更加细致一些
1. try: ... except Exception as e:... 基本可以捕获所有的运行时异常,在工作中很常见.
2. 但是,从经典理论考虑,一般建议尽量捕获多个异常(按照先子类后父类的顺序),并且针对性的写出异常处理代码.
3. 为了避免遗漏可能出现的异常,可以在最后增加 BaseException.
try:
被监控的,可能引发异常的语句块
except Exception01:
处理Exception01的语句块
except Exception02:
处理Exception02的语句块
...
except BaseException: # 最后使用BaseException保险
处理可能遗漏的异常的语句块
try...except...else结构
xxx...else => else是可选的,只要是正常执行完成,没有中途退出,就会执行else子句
# code04_try_except_else.py
def main():
try:
a, b = map(float, input().strip().split())
c = a / b
except Exception as e:
print(type(e), e)
else: # 如果正常执行则最后会执行else子句
print("a/b=", c)
if __name__ == "__main__":
main()
try...except...finally结构
finally是无论发生什么异常都会被执行,通常用来释放try块中申请的资源
try:
...
except Exception as e:
...
finally: # finally 一般就是使用来关闭资源的
f.close()
return语句和异常处理问题
return语句不要出现在异常处理机制中,否则容易有意想不到的效果.
常见异常的解决
常见异常汇总
with上下文管理
with上下文管理器极大的简化了释放资源的操作
1. finally块是否发生异常都会执行,通常我们在finally写释放资源的代码.
2. 其实我们可以通过 with上下文管理,更方便的实现释放资源的操作.
3. with 语法格式:
with 表达式 [as 别名]:
语句块
4. with上下文管理器可以自动管理资源,在with代码块执行完毕后自动还原进入代码之前的现场或上下文. 不论何种原因跳出with块,不论是否有异常,总能保证资源正常释放. => 极大的简化了资源释放的工作,在文件操作,网络通信相关的场合非常常用.
trackback模块
# code05_traceback写入日志文件.py
import traceback
def main():
try:
a = 1 / 0
except:
with open(r"./error.log", "a") as f:
traceback.print_exc(file=f)
if __name__ == "__main__":
main()
自定义异常类
自定义异常 = 继承Exception + raise
1. Python中定义了大量的异常类,虽然这些异常类可以描述编程时出现的绝大多数情况,但有时需要程序员自定义异常.
2. Python允许程序员自定义异常,一般自定义异常类都是运行时异常,通常只要继承Exception或其子类即可. 命名一般以Error,Exception为后缀.
3. 自定义异常由raise语句主动抛出.
# code06_自定义异常类.py
# 自定义异常 = 继承Exception + raise
class AgeException(Exception): # 继承Exception(命名 xxxException/xxxError)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __str__(self):
return "年龄错误!"
def main():
age = int(input("输入一个年龄:").strip())
if not 1 < age < 150:
raise AgeException()
else:
print("年龄正常:",age)
if __name__ == "__main__":
main()