Python基础教程之异常处理详解

前言

最近发现有些东西长时间不用就要忘了,坚持每天复习总结一个小知识点吧~

异常是什么呢?就是在代码执行过程中非预期的执行结果,随着代码越来越复杂,代码中的执行逻辑也会越来越复杂,如果没有处理好异常情况,很有可能造成软件执行错误,导致重大损失。相反,如果合理的处理异常情况,则可以增强软件的稳定性,提高体验感。

异常

Python中,使用异常对象(exception object)来表示代码执行过程中所发生的异常情况,当执行程序爆出错误的时候则会抛出异常。

如果没有正确处理异常,则会终止运行。

你可以想象一下,如果你在开发一款产品时,出现异常而不报告出发生异常的原因,是不是会很难受,也很难解决异常的问题。

为了提高产品的稳定性与灵活性,Python运行开发者捕捉并处理各类异常,一般的内部模块报错如KeyError异常类较为常见,当然也有很多其他的。

错误与异常

简单了解下,Python将代码执行错误分为两类:语法错误(syntax error)和异常(exception)

语法错误

首先,是语法错误的问题。字面可知,代码解析错误。

这种错误通常出现在初学者,主要原因是所执行的代码不符合Python语法的规范,故会报出语法错误导致code停机。

下面给出一个错误示范:

>>> if for not in list(1,2,3,4)
  File "<stdin>", line 1
    if for not in list(1,2,3,4)
       ^
SyntaxError: invalid syntax

从上面的报错中可以知道是语法错误,无效的语法。并且指出了错误的地方在for的位置,很显然iffor不能用在一起。

异常

异常则是在代码执行过程中发现的错误,这是很难提前被发现的,即使代码写的很规范标准,但也可能会出现执行异常的情况。

下面给出一个错误示例。经常被提到的是除数为0的情况,数学运算中,0是不可以作为除数的,如果作为除数则会爆出异常。

>>> a = 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

很明显报错指出0作为除数是不对的。

异常处理

当我们了解了错误和异常后,就得处理异常情况。对于语法错误这种问题多多练习即可避免,对于第二种异常,python给出了try-except语句来处理。

为了更清晰的解释,还是以上面的除数为0的情况作为例子,我们通过添加try-except语句捕捉并处理异常情况:

def division(x, y):
        try:
                return x / y
        except ZeroDivisionError:
                print("0不能作为除数!!!")

然后我们再在terminal调用函数试试:

division(x=1, y=0)

输出:

0不能作为除数!!!

虽然我们的输入是错误的理论应该报错,但是并没有报错而且还返回了一段话。

下面解释一下try-except语句的工作原理:

  1. Try-except中的代码会被正常执行
  2. 如果没有出现异常则跳过except代码块并结束try-except
  3. 如果try-except中的某一句代码出现了问题异常,剩余代码停止执行,如果出现的异常与except所指定的一致,则执行except中的代码块,异常处理结束整个应用程序继续执行
  4. 如果出现的异常与except中指定的不符合,那么则跳出try语句,程序继续抛出异常并终止执行代码

当然,我们写的代码可能不止出现一种异常情况,我们可以将所有异常写在一个except语句中,如下所示:

except (RuntimeError, TypeError, NameError):pass

如果捕捉到异常列表中的任意一项异常表达式,则都会进入except处理。

当然,如果你想对每一种异常进行单独处理也可以一个一个的分开进行处理:

def passpass(x=1):
        try:
                return print(x+x)
        except (RuntimeError, TypeError, NameError):
                pass
        except TypeError:
                print('参数错误啦~')
        except NameError:
                print('名称错误啦~')

今天先到这里明儿再更,去跳绳了,哎,每天卷卷卷,老北京鸡内卷啊~(2022.4.20)

Python的异常类型是可以继承的, 我们在此仅需知道,若except后指定的异常继承自前面异常,由此后面的异常也会被捕捉到,示例如下:

class A(Exception):
        pass
class B(A):
        pass
class C(B):
        pass

for cls in [A, B, C]:
        try:
                raise cls()
        except C:
                print('C')
        except B:
                print('B')
        except A:
                print('A')

则会输出:

A

B

C

相反,如果把except的顺序倒过来,则只会输出A,由于异常B和C都继承来自A,由此在捕捉到B异常后则会终止:

class A(Exception):
        pass
class B(A):
        pass
class C(B):
        pass

for cls in [A, B, C]:
        try:
                raise cls()
        except A:
                print('A')
        except B:
                print('B')
        except C:
                print('C')

输出全为A:

A

A

A

如果所有异常都一一的提取出来,这会很麻烦,此时可以在最后的一个except中不设置异常类型,由此剩下没有被捕获的异常全会被捕捉:

def passpass(x=1):
        try:
                return print(x/x)

        except TypeError:
                print('参数错误啦~')
        except NameError:
                print('名称错误啦~')
        except:
                print('报错啦~自己查')

一个重要功能:else.

else语句则是用来执行一些额外操作,如try代码块中执行了一些文件操作,在else中可以释放资源,else的语法格式如下:

try:
        pass
except:
        pass
else:
        pass

当然我们还可以操作异常,如下:

def passpass(x=1):
        try:
                return print(x/x)

        except TypeError as error:
                print('参数错误啦~', error)
        except NameError:
                print('名称错误啦~')
        except:
                print('报错啦~自己查')

自主抛出异常

开发者在平常的有些情况下并没有执行错误,但是不符合设计逻辑,由此需要开发者主动抛出异常,这时我们需要使用raise语句抛出异常:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere

自定义异常

Python内置异常无法满足开发需求的时候,可以自定义异常。

自定义异常类必须要直接或间接继承自Exception类。自定义异常类可以像其他类一样做任何事情,但原则上要保持简洁,提高一些属性即可。

class Error(Exception):
        """Base class for exception in this module."""
        pass

class InputError(Error):
        """Exception raised for errors in the input.

        Attributes:
                expression -- input expression in which the error occurred
                message -- explanation of the winerror

        """
        def __init__(self, expression, message):
                self.expression = expression
                self.message = message

finally子句

else在代码正常执行后才会被执行的代码块,但有些情况无论代码块是否出现异常都要执行,则需要用到finally语句:

def passpass(x=1):
        try:
                return print(x/x)

        except TypeError as error:
                print('参数错误啦~', error)
        except NameError:
                print('名称错误啦~')
        except:
                print('报错啦~自己查')
        finally:
                print('运算结束~')

总结

原文地址:https://blog.csdn.net/weixin_44333889/article/details/124306492