前言

如果异常未捕捉,系统就会一直将异常传递下去,直到程序由于异常而异常而中断。为了避免出现这种程序异常中断的情况,现在对“危险”的代码段进行异常捕捉。在python语言中,使用try……except语句进行异常捕获。那么这个语句有哪些用法呢?


一、try……except语句的基本用法

try……except语句用于捕获代码块中的异常。在使用try……except语句之前,先看一下不使用该语句的情况。

x = int(input('请输入分子:'))
y = int(input('请输入分母:'))
print('x/y={}'.format(x / y))

执行上面的代码后,分子输入任意的数值,分母输入0,会抛出,会抛出下述所示的异常,从而导致程序奔溃,也就是说,本来正常执行第3条语句(print函数),但由于x/y中的y变量是0,所以直接抛出了异常,因此,第三条语句后面所有的语句都不会被执行。

请输入分子:>? 30
请输入分母:>? 0
Traceback (most recent call last):
  File "D:\ProgramData\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-ece946d564d3>", line 3, in <module>
    print('x/y={}'.format(x / y))
ZeroDivisionError: division by zero

由于用户的输入是不可控的,所以当采集用户输入的数据时,应该使用try……except语句对相关代码进行异常捕捉,尽管异常并不会每次都发生,但这么做可以有备无患。

1、本例通过try……except语句捕捉用户输入可能造成的异常,如果用户输入了异常数据,会提示用户,并要求重新输入数据
x = None
while True:
    try:
        # 如果x已经有了值,表示已经捕捉了异常,那么再次输入数据时,就不需要输入x值了
        if x == None:
            x = int(input('请输出分子:'))
        y = int(input('请输入分母:'))
        print('x/y={}'.format(x / y))
        break
    except:
        print('分母不能为0,请重新输入分母')  # 只有发生异常时,才会执行这行代码

执行上面的代码,分子输入30,分母输入0,按enter键会输出异常提示信息,然后会要求再次输入分母,输入一个非零的数值,如30,按enter键后,会输出x/y的结果。

请输出分子:>? 30
请输入分母:>? 0
分母不能为0,请重新输入分母
请输入分母:>? 30
x/y=1.0

从上面的例子可以了解关于try……except语句的如下几方面内容:

  1. try……except语句是一个代码块,所以try和except后面都要加冒号(:)
  2. try和except之间是正常执行的语句,如果这些代码不发生错误,那么就会正常执行下去,这时except部分的代码时不会执行的。如果try 和except之间的代码发生了错误,那么错误点后面的代码都不会被执行了,而会跳到except子句去执行except代码块中的代码。
  3. 如果except关键字后面没有指定任何异常类,那么except部分可以捕捉任何的异常。

二、捕捉多个异常

我们并不能预估一个代码块到底会不会抛出异常,以及抛出多少中异常。所以需要使用try……except语句捕捉尽可能多的异常,因此,except子句可以包含任意多个。不过程序员并不能准确估计一代码块抛出的异常种类,所以使用具体异常类来捕捉异常,有可能会遗漏某个异常,在这种情况下,当抛出这个当抛出这个被遗漏的异常后,程序还是会崩溃,所以比较保险的做法是最后一个except子句不使用任何异常类,这样就会捕捉其他所有未指定的异常没从而让程序更加健壮。

try:except 异常类1:
	…
except 异常类2:
	…
	…
except 异常类n:
	…
except#捕捉其他未指定的异常
  1. 本例将通过SpecialCale类的三个方法(add、sub、mul)和raise语句抛出两个自定义的异常(NegativeException和ZeroException),div方法可能会派出内建的NegativeException异常。这位三个会通过except子句进行捕捉,最后会使用except语句捕捉其他未指定的异常。本例的很逻辑在while循环中,通过console输入表达式(如add),动态调用SpecialCale类的相应方法,不管是抛出异常还是正常掉调用,都会重新要求输入新的表达式,直到输入“end”命令退出while 循环。
# 自定义异常类,表示操作数或计算结果为负数时抛出异常
class NegativeException(Exception):
    pass


# 自定义异常类,表示操作数为0时抛出的异常
class ZeroException(Exception):
    pass


class SpecialCalc:
    def add(self, x, y):

        # 当x和y有一个小于0时抛出NegativeException异常
        if x < 0 or y < 0:
            raise NegativeException
        return x + y

    def sub(self, x, y):

        # 当x-y的差值是负数时或抛出NegativeException异常
        if x - y < 0:
            raise NegativeException
        return x - y

    def mul(self, x, y):

        # 当x和y至少有一个是0是抛出ZeroException异常
        if x == 0 or y == 0:
            raise ZeroException
        return x * y

    def div(self, x, y):
        return x / y


while True:
    try:
        # 创建SpecialCalc实例
        calc = SpecialCalc()
        # 从console输入表达式
        expr = input("请输入要计算的表达式,例如add(1,2):")
        if expr == 'exit':
            break
        # 使用eval函数动态执行输入的表达式,前面需要加上‘calc.’前缀
        # 因为这些方法都属于SpecialCalc类
        result = eval('calc.' + expr)
        # 在控制台输出结果,保留小数点后两位
        print('计算结果:{:.2f}'.format(result))
    except NegativeException:
        print('********负数异常******')
    except ZeroException:
        print('******操作数为0异常****')
    except ZeroDivisionError:
        print('******分母不能为0******')
    except:
        print('****其他异常**********')

运行上面的程序,并输入不同的表达式来引发两个定制的异常和ZeroDivsionError,以及输入错误的表达式以便引发其他异常,运行结果如下:

请输入要计算的表达式,例如add(1,2):add(1,3)
计算结果:4.00
请输入要计算的表达式,例如add(1,2):add(-5,3)
********负数异常******
请输入要计算的表达式,例如add(1,2):sub(4,6)
********负数异常******
请输入要计算的表达式,例如add(1,2):div(40,0)
******分母不能为0******
请输入要计算的表达式,例如add(1,2):div(53,21)
计算结果:2.52
请输入要计算的表达式,例如add(1,2):abvcd
****其他异常**********
请输入要计算的表达式,例如add(1,2):exit

Process finished with exit code 0

插曲:在调试上面的代码的时候由于自己的粗心,中间一个标点符号打错了,然后持续运行之后出现这样的情况。

请输入要计算的表达式,例如add(1,2):add(1,2)
****其他异常**********
请输入要计算的表达式,例如add(1,2):sub(4,2)
****其他异常**********
请输入要计算的表达式,例如add(1,2):div(5,2)
****其他异常**********
请输入要计算的表达式,例如add(1,2):exit

Process finished with exit code 0

直到我祛除了最后一行捕捉其他异常的错误信息之后再次进行调试,才发现了问题在哪。

请输入要计算的表达式,例如add(1,2):add(1,3)
Traceback (most recent call last):
  File "D:/pyproject/leetcode/editor/LeetCode学习.py", line 66, in <module>
    print('计算结果:{:.2f}'.format(result))
KeyError: ':'

Process finished with exit code 1

从上面的错误信息中能够看到错误的代码以及代码所在的行数等,是我在格式化输出的时候没有冒号错误使用了中文的冒号。所以,在捕捉异常的时候,如果代码出现了其他异常,注释掉最后一个捕捉所有异常的代码,再次运行也许是一个不错的选择。


三、用同一个代码块处理多个异常

虽然代码块可能抛出多个异常牡丹石有时多个异常的处理程序可以是一个,在这种情况下,如果用多个except子句捕捉这些异常,就需要在每一个except子句中使用同一段代码处理这些异常。为了解决这个问题,except子句允许指定多个异常,这样指定后,同一个except子句就可以捕捉多个异常了。

try:
	……
except(异常1,异常2,异常3,……异常n)
  1. 本例定义了一个raiseException函数,用于随机抛出三个自定义异常,然后用同一个except子句捕捉异常。
# 第一个自定义异常类
class CustomException1(Exception):
    pass


# 第二个自定义异常类
class CustomException2(Exception):
    pass


# 第二个自定义异常类
class CustomException3(Exception):
    pass
#导入random模块
import random
#随机抛出前三个自定义异常
def raiseExecption():
    n=random.randint(1,3)
    print('抛出CustomException{}异常'.format(n))
    if n==1:
        raise CustomException1
    elif n==2:
        raise CustomException2
    else:
        raise CustomException3

try:
    raiseExecption()
#使用except子句同时捕捉这三个异常
except(CustomException1,CustomException2,CustomException3):
    print('*****执行异常处理结果******')

运行结果如下:

第一次运行结果:
抛出CustomException2异常
*****执行异常处理结果******

第二次运行结果:
抛出CustomException3异常
*****执行异常处理结果******



总结

以上就是本文要介绍大的主要内容,在前面一篇文章中主要介绍了什么是异常,如何创建异常类以及如何主动抛出异常,本文则重点介绍如何捕捉异常,try……except语句的基本用法、以及如何捕捉多个异常等

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐