Python · Day 18

多种异常与
安全除法函数

第十八天,我在昨天的 ValueError 基础上继续学习多种异常处理,让输入错误和除零错误分别进入对应分支,并补全 try / except / else / finally 的执行路线。

一个 try,处理两种不同异常

除法程序至少有两种常见错误:abc 无法转换成整数,会产生 ValueError;除数为 0 时,除法运算会产生 ZeroDivisionError。它们应该由不同的 except 分别处理。

day18.py
try:                                                # 尝试输入整数并进行除法
    number1 = int(input("请输入第一个数字:"))    # 转换第一个整数
    number2 = int(input("请输入第二个数字:"))    # 转换第二个整数
    result = number1 / number2                     # 除数为0时产生除零错误
    print("计算结果:", result)                  # 没有异常时输出结果
except ValueError:                              # 接住整数转换错误
    print("输入错误,请输入整数")                # 显示类型错误提示
except ZeroDivisionError:                       # 接住除数为0的错误
    print("计算错误,除数不能为0")               # 显示除零错误提示

实际分别测试了 10 ÷ 2abc10 ÷ 0,得到 5.0、整数输入提示和除数不能为 0 的提示。输入 abc 后不会继续询问第二个数字,因为异常发生时会立即离开 try,跳到对应的 except

加入循环:错误后重新输入

把异常结构放进 while True 后,发生错误时本轮代码执行完毕,自然进入下一轮;成功计算后才执行 break。因此两个 except 末尾不写 continue 也能重新询问。

break 的缩进位置很重要。如果把它放在 except ZeroDivisionError 下面,输入 0 后反而会立即结束循环。

try 后的 else

这里的 else 不需要手写判断条件。Python 会自动检查 try 是否完整执行:有异常时进入相应的 except,没有异常时跳过所有 except 并进入 else

day18.py
while True:                                     # 不断尝试直到成功计算
    try:                                        # 尝试转换输入和进行除法
        number1 = int(input("请输入第一个数字:")) # 获取第一个整数
        number2 = int(input("请输入第二个数字:")) # 获取第二个整数
        result = number1 / number2                # 尝试得到浮点数结果
    except ValueError:                        # 输入转换失败时进入这里
        print("输入错误,请输入整数")              # 提示后开始下一轮
    except ZeroDivisionError:                 # 除数为0时进入这里
        print("计算错误,除数不能为0")             # 提示后开始下一轮
    else:                                      # try没有异常时进入这里
        print("计算结果:", result)                # 输出成功结果
        break                                    # 成功后结束循环

同一次运行中依次输入 abc10 ÷ 010 ÷ 4,前两次分别进入不同的异常分支,最后得到 2.5 并结束循环。

函数中使用 return 送出结果

安全除法被封装进 input_division() 后,成功路线改用 return result。它不仅结束循环和函数,还把结果交给外部的 answer;如果只使用 break,函数没有返回值,answer 会得到 None

safe_divide.py
def input_division():                         # 定义安全除法函数
    while True:                                  # 错误时持续重新询问
        try:                                      # 尝试转换输入和进行除法
            number1 = int(input("请输入第一个数字:")) # 获取第一个整数
            number2 = int(input("请输入第二个数字:")) # 获取第二个整数
            result = number1 / number2            # 尝试计算浮点数结果
        except ValueError:                    # 接住整数转换错误
            print("输入错误,请输入整数")          # 提示后继续循环
        except ZeroDivisionError:             # 接住除零错误
            print("计算错误,除数不能为0")         # 提示后继续循环
        else:                                  # 没有异常时进入这里
            return result                       # 返回结果并结束函数

测试中先后触发了输入错误和除零错误,最后输入 92,函数返回 4.5,外部变量 answer 成功接住结果。

finally:每条路线最后都会经过

finally 无论是否发生异常都会执行。即使 else 中已经遇到 return result,Python 仍会先执行 finally,然后才把结果真正返回给外部。

safe_divide.py
        else:                                  # try没有异常时进入这里
            return result                       # 准备把结果返回给外部
        finally:                               # 成功或失败都会进入这里
            print("本次输入处理结束")          # 每一轮结束前都输出提示

最终测试依次经历文字输入、除数为 0 和有效除法。每次尝试后都输出“本次输入处理结束”;成功计算 10 ÷ 2 时,先执行 finally,再输出函数外的最终结果 5.0

今天真正掌握的执行路线

  • ValueError:输入内容不能转换成需要的数值类型
  • ZeroDivisionError:除法的除数为 0
  • else:只在 try 没有异常时执行
  • finally:正常完成、发生异常、遇到 breakreturn 时都会执行
  • return:结束函数并把结果送给调用位置;break 只能结束循环

今天不只是让程序“不报错”,而是开始区分错误类型:先判断发生了哪一种异常,再进入对应的处理路线。