Python的学习心得和知识总结(八)|Python异常及异常处理机制

代码出现异常怎么看待

开始之前我们先休息一下,久坐成疾!而且以后还可能犯老年痴呆(我还年轻),接下来我们先讲上3个笑话:

1、在公司听到过一句最惨绝人寰骂人的话:“你TM就是一个没有对象的野指针!”
2、C程序员看不起C++程序员, C++程序员看不起Java程序员, Java程序员看不起C#程序员,C#程序员看不起美工,周末了,美工带着妹子出去约会了,一群程序员还在加班!
3、我是一个苦b的程序员,今晚加班到快通宵了,困得快睁不开眼了,女同事很关心,问我要不要吃宵夜。我没好气地说,宵夜就算了,能让我睡一觉就行了。女同事红着脸说了句讨厌啊,然后坐在我身边不动,好像距离我很近,搞得我很紧张,难道她发现我的程序出了bug?
在这里插入图片描述
乐呵完之后,开始今天的学习!经常听到人说:宁可相信世上有鬼都不可以相信女人的嘴!后来我听到了一句:我写的代码就和我的长相一样,毫无bug可言(一顿大笑)。从那以后,我再也不跟她玩了,骗人!

写代码写多了,能写出一版实现功能需求的代码真的不难!可是然后呢?

1、代码是否可读以及优美?
2、性能消耗也是否考虑到?
3、边界非法情况怎么解决?
4、代码出现异常如何处理?

凡此等等,我也工作了这么长时间了。谈一谈心里面的感受:讲真心话,我写的任何一段代码 如果真的挖心思去挑毛病,就会正如鲁迅先生的话:海绵里面的水,挤一挤总还是有的!倘若你让我写出一个能实现要求且无懈可击的代码,恕在下办不到!

代码出现错误Error或异常Exception特么真是一件和我们喝白开水一样正常的事情,有问题就解决问题 车到山前必有路,逢山开路 遇水架桥,大不了重写了事!

而高级语言的异常处理机制就有如下的效果(为我们提供如下便利):

  1. 避免逻辑代码和错误处理代码 混在一起
  2. 程序开发需要考虑的Exception实在是太多,减轻开发压力(在背后该处理的还是得写代码
  3. 真的出现异常,依然可以正确的去执行接下来的程序(并不会因为异常而终止整个程序运行)

Python的异常处理机制

下面就看一下Python里面的异常处理机制:它里面引入了大量的异常类

  1. 功能:描述和处理异常
  2. 类中提供该类异常的信息,以及对该类异常的处理方法

下面来看一下Python提供的异常类的继承层次:(图片来源于网络)
在这里插入图片描述
OK,下面就依次对上面各个异常类做个简短的说明:

类名 描述说明
BaseException 1 所有异常的基类
SystemExit 2 解释器请求退出
KeyboardInterrupt 2 用户中断执行(Ctrl + c)
GeneratorExit 2 生成器generator 发生异常,通知退出
Exception 2 常规错误的基类
StopIteration 3 迭代器没有更多的值
StandardError 3 所有的内建标准异常的基类
BufferError 4
ArithmeticError 4 所有数值计算错误的基类
FloatingPointError 5 浮点计算错误
OverflowError 5 数值运算超出最大限制
ZeroDivisionError 5 除(或取模)零 (所有数据类型)
AssertionError 4 断言语句失败
AttributeError 4 对象没有这个属性
EnvironmentError 4 操作系统错误的基类
IOError 5 输入/输出操作失败
OSError 5 操作系统错误
WindowsError 6 系统调用失败
VMSError 6
EOFError 4 没有内建输入,到达EOF 标记
ImportError 4 导入模块/对象失败
LookupError 4 无效数据查询的基类
IndexError 5 序列中没有没有此索引(index) 或者说索引值越界
KeyError 5 映射中没有这个键 ,如字典的关键字不存在
MemoryError 4 内存溢出错误(对于Python 解释器不是致命的)
NameError 4 未声明/初始化对象 (没有属性) ,如访问一个没有声明的变量
UnboundLocalError 5 访问未初始化的本地变量
ReferenceError 4 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 4 一般的运行时错误
NotImplementedError 5 尚未实现的方法
SyntaxError 4 Python 语法错误
IndentationError 5 缩进错误
TabError 6 Tab 和空格混用
SystemError 4 一般的解释器系统错误
TypeError 4 对类型无效的操作,如123+“hello”
ValueError 4 传入无效的参数,如 int(“songbaobao”)
UnicodeError 5 Unicode 相关的错误
UnicodeDecodeError 6 Unicode 解码时的错误
UnicodeEncodeError 6 Unicode 编码时的错误
UnicodeTranslateError 6 Unicode 转换时的错误
Warning 3 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

举一个例子,如下:
在这里插入图片描述
如上图所示:因为在Python里面 一切皆对象,即使是异常也是采取对象的方式来处理的。整个抛出异常的过程如下:上面除以0,发生异常。解释器就会生成一个可以代表该异常的一个对象,即生成一个ZeroDivisionError类型的异常对象;停止当前执行路径;并把该异常对象提交给解释器。

接下来就是捕获异常的过程:当解释器获得这个异常之后,就会寻找相应的代码来处理该异常;处理完之后程序继续执行。
在这里插入图片描述
控制台打印的信息如上,需要认真对待!

处理异常的try except块

OK,我们在定位到上面这个程序的异常原因之后。接下来我们就实质地解决这个异常,下面是Python里面最常见的异常处理结构

try:
	可能会出现异常的代码段(上面的那一行代码就可以放在这里)
except BaseException [as e]:
	异常处理的代码段

上面的try块里面是 可能 会出现异常的代码;except块则是 如果出现异常,捕获并处理的代码。

1、当然如果try块没有出现异常,则except块的代码就可以被忽略,继而向下继续进行。
2、如果try块里面发生异常,则跳过try块的剩余代码。继而直接跳到except块里面处理异常。等到异常处理完成,则会继续执行except块之后的剩余代码(不是再回到try里面
3、BaseException是所有异常类的基类,当然对付我们平时的问题 Exception也是足够的。

单个except结构实例

在这里插入图片描述
上面的myexcept是这个异常对象,print的只是其描述信息!如下:

print(type(myexcept))
<class 'ZeroDivisionError'>

如果要是没有异常的话,则如下所示:(异常处理模块将会被完全跳过)
在这里插入图片描述
下面来看一个简单的实例:
在这里插入图片描述

多个except结构实例

上面的程序毕竟是比较简单的,然而在实际的工作中 并不一定是只有这一种异常!为了尽可能的捕获到 将会出现的多个异常(如有继承关系则按照先子类后父类的顺序),我们会通常使用多个except块 使用格式如下:

try:
	可能会发生异常的代码段
except Exception1:
	处理Exception1的代码段
except Exception2:
	处理Exception2的代码段
…… :
	…… …… …… …… …… …… …… …… 
except BaseException:
	处理可能被遗漏的异常

之所以按照先子后父的顺序:倘若父类放在前面,子类在后面。即使是属于子类的异常,也会被前面的父类所捕获。下面就演示一个具体的实例:
在这里插入图片描述
程序的全部打印信息为:

请输入一个数据作为除数或者结束标志EOF:4
请输入一个数字作为被除数:2
你所输入的合法数字的商为: 2.0
请输入一个数据作为除数或者结束标志EOF:2
请输入一个数字作为被除数:0
该异常类型: <class 'ZeroDivisionError'> ; 异常信息为: division by zero
请输入一个数据作为除数或者结束标志EOF:1
请输入一个数字作为被除数:a
该异常类型: <class 'ValueError'> ; 异常信息为: invalid literal for int() with base 10: 'a'
请输入一个数据作为除数或者结束标志EOF:EOF
正常退出程序!
输入完成!

Process finished with exit code 0

因为上面的ZeroDivisionError和ValueError 没有继承关系,放在前后没有影响!下面就详细的看一下这两个错误的继承关系图:
在这里插入图片描述
在这里插入图片描述
不过这个功能是只有在安装Pycharm专业版才有的,注:可能因为我比较穷吧!一般软件基本上也都是才有某种方式来使用的,今天就为了使用一个最新的专业版花了很长的时间才完成!若小伙伴需要的话,我可以免费帮大家做,不过我们还是要尊重原正版开发人员的付出

OK,话不多说 继续学习!

try-except结构的补充型

当然因为功能或者需求的需要,上面的try-except结构有时候无法完成任务。

下面就介绍第一种新的结构:

try except else结构:

try:
	可能会发生异常的代码段
except Exception1:
	处理Exception1的代码段
except Exception2:
	处理Exception2的代码段
…… :
	…… …… …… …… …… …… …… …… 
except BaseException:
	处理可能被遗漏的异常
else:
	其他的一些代码逻辑

对上面的语法格式解释一下:

1、若是try块没有出现异常,则执行完 就执行else块
2、若是try块出现异常,则执行except块 就不再执行else块
3、在逻辑上else块与except块是并列关系

实例如下:
在这里插入图片描述
下面就介绍第二种新的结构:

try except finally结构:

我们设想一下这样的一个场景:在try块里面分配了一些资源(内存 网络连接 文件等),可是try块出现异常下面的释放工作可能没有做,若是except里面也没有(也可能不知道分配了什么资源)。这样的话就可能会造成资源的泄露or浪费,因此这种try except finally结构就很好的解决了问题。

其使用方式:

try:
	可能会发生异常的代码段
except Exception1:
	处理Exception1的代码段
except Exception2:
	处理Exception2的代码段
…… :
	…… …… …… …… …… …… …… …… 
except BaseException:
	处理可能被遗漏的异常
else:
	其他的一些代码逻辑
finally:
	这里面的代码无论是否发生异常都会执行

对上面的语法格式解释一下:

1、若是try块没有出现异常,则执行完 就执行else块
2、若是try块出现异常,则执行except块 就不再执行else块
3、在逻辑上else块与except块是并列关系;不管try块里面的代码无论是否发生异常都会执行finally里面代码

实例如下:
在这里插入图片描述
下面我们来看一下之前I/O流文件操作的例子:
在这里插入图片描述
上面第二种情况:文件不存在,在open的时候就会出现异常 然后就去执行except块里面的代码,最后也执行finally块的代码。可是大家不要忘了,在执行fp.close() 的时候也是会有问题的,如下:
在这里插入图片描述
所以应该在 finally块里面 也加上 异常处理的逻辑:(这样就算是一个比较完美的程序了)
在这里插入图片描述

Python常见异常的说明

在这里插入图片描述
这个我们已经在上面的表格中一一列举说明了,大家只要记住这个图里面的继承关系和这个单词的意思对应就可以了!

Python with上下文管理

我们上面也说了finally块可以帮助我们释放在try块里面使用的资源,以防止资源泄露或者浪费。而这种方式在使用上并不是十分的简便,我们接下来引入新的释放资源的操作:with 上下文管理,其语法结构如下:

with context_expression [as variable]:
	代码段
	……

with上下文管理可以自动的管理资源:在with代码段执行完成之后,自动还原(恢复)进入该代码之前的现场or上下文。

1、无论是否发生异常,跳出with块 它也总能够保证资源的正常释放
2、适用于文件操作 和 网络通信的场景下

在这里插入图片描述
如上把之前的文件操作出现的异常,这次使用with来实践一下。

这里需要特别说明的一点:with的作用不在于是取代前面的try except finally结构的,而是作为一种方便对文件和网络资源操作的补充。

Python的trackback模块

我们在开发C/C++项目的时候,因为代码量十分庞大而导致调试起来也是非常的不方便。于是为了追踪出现问题时候的报错打印信息,往往可以在代码里面加上打印日志等。而Python则直接为我们提供了更加方便的打印出错信息模块:Trackback模块

我们的一个程序如下:

#coding=utf-8

while True:
    end_str=str(input("请输入一个数据作为除数或者结束标志EOF:"))

    if end_str=="EOF":
        print("正常退出程序!")
        break
    try:
        number1=int(end_str)
        number2=int(input("请输入一个数字作为被除数:"))
        result=number1//number2

    except ZeroDivisionError as myexception:
        print("该异常类型为:",type(myexception),";","异常信息为:",myexception)
    except ValueError as myexception1:
        print("该异常类型为:",type(myexception1),";","异常信息为:",myexception1)

    except BaseException:
        print("其他的异常类型!")
    else:
        print("你所输入的合法数据的商为:",result)
    finally:
        print("*"*35)

print("输入完成!")

结果打印如下:
在这里插入图片描述
上面虽然根据每个异常类型做了相应的处理,可是我们若是想要如下的trackback信息打印 如下:
在这里插入图片描述
在这里插入图片描述
若是我们需要将上面的错误打印也输出到一个文件里面(类似于日志):
在这里插入图片描述

Python的自定义异常类

在实际的程序开发中,我们需要去自己定义一个异常类。这个自定义的异常类一般都是运行时异常,通常继承Exception类或者它的一些子类即可完成。

注:自定义的异常类的命名规则都是一般以Error Exception为后缀的,它是由raise语句主动抛出:
在这里插入图片描述

来调试一个Python程序

调试这个东西,我的感觉:又爱又恨。因为本人的工作是做PostgreSQL内核开发,一个C代码在98%以上的庞大系统,近千万行的代码量。因为做的是二次开发,难免整个过程中出现各种各样的问题,每次遇到一个问题之后 即使是调试也要付出相当大的精力:

1、看看这个问题是个啥(心里有个底)
2、大致猜一下原因是啥(估调查方向)
3、立刻要复现这个问题(得问题场景)
4、阅读代码并着手调试(查漏洞原因)
5、开始确定修改的目标(选最优方案)
6、动手修复漏洞并测试(不会太复杂)
7、反复验证并提交代码(自测很重要)

OK,我们之前已经使用过VS2019来调试过Python代码,说实话 跟调试C/C++语言差的不多。那我们今天就在PyCharm上面调试一下程序:

在进行调试的时候,我们大家都知道 要打断点,等到程序运行到并触发这个断点。系统就会挂起并停止执行(此时的参数传递都已经OK了),我们接下来做的就是一步一步 or 一步一跳的调试这个程序。来确定程序的运行是否符合我们的预期,并复现问题 查明原因!

OK,我们先布置一个简单的问题场景,如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
还可以设置一下变量的值,如下:
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43949535/article/details/106338290