【Python】笔记21(Deal with Error)

错误来源于程序编写的问题(Bug)、用户输入造成的(检查用户输入)、磁盘满了、网断了(异常)等等...

'open(),打开文件的函数,传入的是文件的路径,成功打开返回文件描述符,出错时返回-1。

用错误码来表示是否出错十分不便,函数本身返回的正常结果会和错误码混在一起,定义时就要用大量的代码来判断是否出错,出错了,函数可能还要一级一级上报,知道某个函数可以处理该错误。所以有错误处理机制:try...except...finally

【Python】笔记21(Deal with Error)_第1张图片

try可以用来测试代码,当try下方的某行代码执行出错, 后续代码就不会继续执行,而是跳转到except语句块执行,如果有finally语句块,执行完except后还会继续执行finally。如果没有发生错误。except语句就不会被执行,但若有finally(可以没有),则一定会被执行。

如例子中的10/0是一个错误,这时下方的print就不继续执行了;由于except捕获到ZeroDivisionError,因此被执行。

不同类型的错误,可以由不同的except语句块处理。捕获到不同的错误,就会执行该错误下except语句下的代码。如果没有错误发生,还可以加一个else,当没有错误发生时,就会自动执行else语句。    这可以理解成if的多支判断。

错误其实是class,Python所有的错误类型都继承自BaseException,都是从BaseExceotion类派生的(常见的错误类型和继承关系),而且将子类错误都捕获。如

【Python】笔记21(Deal with Error)_第2张图片

因为UnicodeError是ValueError的子类,第一个except会吧UnicodeError当做ValueError捕获。

try...except捕获错误可以跨越多层的调用,如

【Python】笔记21(Deal with Error)_第3张图片

以上的函数调用关系是main()调用foo(),foo()调用bar()。如果bar()出错了,main()就可以捕获到并处理,而不需要在每个可能出错的地方去捕获错误,只要在合适的层次捕获就可以了。

'可见ZeroDivisionError是Exception的子类


如果错误没有被捕获,它会一直向上上报,最后被Python解释器捕获,打印错误信息,然后退出。

【Python】笔记21(Deal with Error)_第4张图片

如以上错误,最后三行之前,一直在说明main()出错了,原因在于bar('0'),而bar('0')出错原因在于return foo(s)*2,而这个出错的原因又在于return 10/int(s),这是错误的源头,最后打印出了BaseException类中有的错误。这就是错误堆栈。


logging模块,记录错误信息。在except语句块中加入logging.exception()

同样是出错,但是程序打印完整错误信息后会继续执行,并正常退出。配置后,logging还可以把错误记录到日志文件里。


抛出错误,Python对错误信息进行错误报告都是类和实例编写的结果,我们也可以通过raise语句结合if判断编写一个错误类和实例。

【Python】笔记21(Deal with Error)_第5张图片

执行后,可以像上面错误堆栈的方式一样跟踪到自定义错误。

例子中的FooError是ValueError的子类。在自定义错误类型时,应尽量选择Python已有的内置的错误类型为父类。


【Python】笔记21(Deal with Error)_第6张图片


【Python】笔记21(Deal with Error)_第7张图片

在执行后,我们会发现bar()定义中的except语句被执行了,foo(s)中的raise也执行了。既有except的捕获,也有raise的错误抛出。其实捕获错误的目的只是记录一下,便于后续追踪。但当前函数只是捕获而没有进行处理,所以恰当的方式应该是往上层上报,让顶层调用者去调用。

'raise语句不带参数的话,就会把当前错误原样抛出,抛出后就会向上层上报,让Python解释器来处理。

【Python】笔记21(Deal with Error)_第8张图片

'在except中raise一个Error,可以把一种类型的错误转化成另一种类型

但绝不应该吧一个IOError转换成毫不相干的ValueError。


重要的是,出错时会分析错误,并定位到发生错误的代码位置。

你可能感兴趣的:(【Python】笔记21(Deal with Error))