第二章 · 流程控制

异常处理

本节目标

学完这一节,你会知道:

  1. 什么是异常,为什么程序会因为错误而停止
  2. 如何用 try/except 捕获错误
  3. 如何分别处理不同类型的错误
  4. elsefinally 分别什么时候执行
  5. 如何写一个不会轻易崩掉的小计算器

异常处理不是为了隐藏错误,而是为了让程序遇到可预期的问题时,能给用户一个清楚的提示。

先跑一个例子

新建文件 safe_divide.py,写入:

try:
    number = int(input("请输入一个整数:"))
    result = 100 / number
    print(f"100 / {number} = {result}")
except ValueError:
    print("输入错误:请输入整数")
except ZeroDivisionError:
    print("数学错误:不能除以 0")

运行:

python3 safe_divide.py

分别试试输入:

  • 20
  • abc
  • 0

你会发现,程序不会直接崩溃,而是输出不同提示。

为什么需要异常处理?

没有异常处理时,用户输入不符合预期,程序就会报错停止:

number = int(input("请输入一个整数:"))
print(100 / number)

如果用户输入 abcint("abc") 会失败。

如果用户输入 0100 / 0 会失败。

这些运行时出现的问题,就叫异常。

try / except 基础

基本结构:

try:
    可能出错的代码
except 错误类型:
    出错后执行的代码

例子:

try:
    age = int("abc")
except ValueError:
    print("转换失败")

try 里的代码如果没有出错,except 不会执行。

try 里的代码如果出错,并且错误类型匹配,程序会进入对应的 except

常见异常类型

异常 常见原因
ValueError 值不合适,比如 int("abc")
TypeError 类型不合适,比如 "a" + 1
ZeroDivisionError 除以 0
IndexError 列表索引超出范围
KeyError 字典里没有这个键
FileNotFoundError 文件不存在
AttributeError 对象没有这个属性或方法

刚开始不用背,遇到报错时先看最后一行的异常名字。

捕获多种异常

不同错误可以给不同提示:

try:
    number = int(input("请输入数字:"))
    result = 10 / number
    print(result)
except ValueError:
    print("请输入有效数字")
except ZeroDivisionError:
    print("不能除以 0")

也可以获取错误信息:

try:
    int("abc")
except ValueError as error:
    print(f"错误信息:{error}")

as error 会把异常对象保存到变量 error

else 和 finally

else:只有 try 没出错时才执行。

finally:不管有没有出错,最后都会执行。

try:
    number = int(input("请输入整数:"))
except ValueError:
    print("输入不是整数")
else:
    print(f"转换成功:{number}")
finally:
    print("程序结束")

适合用 finally 做收尾工作,比如关闭文件、断开连接。

raise:主动抛出异常

有时你想主动告诉程序:“这个值不合法”。

def check_age(age):
    if age < 0:
        raise ValueError("年龄不能为负数")
    if age > 150:
        raise ValueError("年龄不合理")
    return "年龄正常"

try:
    print(check_age(-1))
except ValueError as error:
    print(error)

刚入门时,先学会读懂 raise。以后写项目时,它会经常用来校验数据。

逐行拆解

再看开头的例子:

try:
    number = int(input("请输入一个整数:"))

这一行可能因为用户输入非数字而出错。

result = 100 / number

这一行可能因为 number 是 0 而出错。

except ValueError:
    print("输入错误:请输入整数")

如果转换整数失败,执行这里。

except ZeroDivisionError:
    print("数学错误:不能除以 0")

如果除以 0,执行这里。

自己改一改

safe_divide.py 改成循环版本:

while True:
    text = input("请输入一个整数,输入 q 退出:")

    if text == "q":
        print("再见")
        break

    try:
        number = int(text)
        result = 100 / number
        print(f"结果:{result}")
    except ValueError:
        print("请输入整数")
    except ZeroDivisionError:
        print("不能除以 0")

然后继续改:

  1. 支持输入大写 Q 退出
  2. 结果保留两位小数
  3. 每次计算结束后输出一条分隔线

实战:安全的计算器

新建文件 safe_calculator.py

try:
    a = float(input("第一个数字:"))
    op = input("运算符(+ - * /):")
    b = float(input("第二个数字:"))

    if op == "+":
        result = a + b
    elif op == "-":
        result = a - b
    elif op == "*":
        result = a * b
    elif op == "/":
        if b == 0:
            raise ZeroDivisionError("除数不能为 0")
        result = a / b
    else:
        raise ValueError("不支持的运算符")

    print(f"结果:{result}")
except ValueError as error:
    print(f"输入错误:{error}")
except ZeroDivisionError as error:
    print(f"数学错误:{error}")

这个例子把输入转换、运算符判断、除零检查放在了一起。

常见错误

1. 把太多代码放进 try

不推荐:

try:
    name = input("名字:")
    age = int(input("年龄:"))
    print("很多不相关的代码")
except ValueError:
    print("年龄错误")

尽量只把可能出错的关键代码放进 try,这样更容易定位问题。

2. 裸 except

不推荐:

try:
    number = int(input("数字:"))
except:
    print("出错了")

推荐捕获具体异常:

except ValueError:
    print("请输入数字")

3. except 顺序太宽泛

如果写了 except Exception,它应该放在最后。

try:
    number = int("abc")
except ValueError:
    print("值错误")
except Exception:
    print("其他错误")

4. 以为异常处理能修复所有问题

异常处理只能处理已经预想到的错误。代码逻辑本身错了,还是要回去修改逻辑。

小练习

练习 1:安全年龄输入

让用户输入年龄:

  • 如果不是整数,提示“请输入整数”
  • 如果小于 0,提示“年龄不能为负数”
  • 否则输出年龄

练习 2:安全取列表元素

给定:

names = ["小明", "小红", "小刚"]

让用户输入索引,输出对应名字。如果索引越界,提示“没有这个编号”。

练习 3:安全除法

让用户输入两个数字,输出第一个数字除以第二个数字的结果。处理非数字和除以 0 两种情况。

参考答案

练习 1:

try:
    age = int(input("请输入年龄:"))
    if age < 0:
        print("年龄不能为负数")
    else:
        print(f"年龄:{age}")
except ValueError:
    print("请输入整数")

练习 2:

names = ["小明", "小红", "小刚"]

try:
    index = int(input("请输入编号:"))
    print(names[index])
except ValueError:
    print("请输入整数编号")
except IndexError:
    print("没有这个编号")

练习 3:

try:
    a = float(input("第一个数字:"))
    b = float(input("第二个数字:"))
    print(a / b)
except ValueError:
    print("请输入数字")
except ZeroDivisionError:
    print("不能除以 0")

小结

这一节你学会了:

  1. 异常是程序运行时出现的问题
  2. try/except 可以捕获并处理异常
  3. 不同异常可以写不同 except
  4. else 在没有异常时执行,finally 总会执行
  5. raise 可以主动抛出异常

下一章我们会学习函数。函数可以把一段代码封装起来,让程序更清楚、更容易复用。

报错不是敌人,它只是说话有点直

异常处理会让程序遇到意外时不至于原地趴下。你可以故意输入 abc、0、奇怪的内容,看看 try/except 怎么接住它们。马哥提醒你:会读报错的人,学编程会少很多内耗。

讨论 (0)

还没有评论,来抢沙发吧!