程序跑着跑着突然中断,终端甩出一长串红色报错——这就是异常。本篇从「求两个整数的商」出发,讲清异常是什么、怎么读堆栈,以及 try-except 怎么用。
本篇目录
1. 从一个小需求说起
2. 什么是异常
3. 如何阅读异常信息
4. 用 if-else 堵漏洞及其局限
5. 异常类的继承体系
6. try-except 基础语法
7. 捕获指定异常
1. 从一个小需求说起
需求:键盘录入两个整数,求商。
num1 = int(input("请录入第一个数:"))num2 = int(input("请录入第二个数:"))print(f"商:{num1 // num2}")正常输入 10 和 2 时:
输入稍有偏差就会崩溃:
| |
|---|
| ValueError |
| ZeroDivisionError |
| ValueError |
业务逻辑很简单,真正让程序挂掉的是运行时错误事件——异常。
2. 什么是异常
**异常(Exception)**是程序运行时发生的错误事件,会打断正常执行流程。一旦产生且未被处理,解释器通常会打印堆栈信息,然后终止当前程序。
可以把它理解成:
1. 某行代码触发了错误条件
2. 解释器创建对应的异常对象
3. 沿着调用栈向上寻找能处理它的 except
4. 找不到就让程序异常退出
常见触发源:非法输入、除零、下标越界、文件不存在、网络超时、类型不匹配等。
3. 如何阅读异常信息
以除零为例:
终端大致会输出:
Traceback (most recent call last): File "demo.py", line 1, in <module> print(10 // 0)ZeroDivisionError: integer division or modulo by zero阅读顺序建议自下而上:
| |
|---|
| 如 ZeroDivisionError,先看「出了什么错」 |
| |
| |
File "...", line N | |
养成习惯:先看异常类型和最后一行描述,再顺着堆栈找自己的代码行。
4. 用 if-else 堵漏洞及其局限
可以在转换和除法前手动校验:
user_input1 = input("请录入第一个数:")if user_input1.isdigit(): num1 = int(user_input1) user_input2 = input("请录入第二个数:") if user_input2.isdigit(): num2 = int(user_input2) if num2 != 0: print(f"商:{num1 // num2}") else: print("错误:除数不能为零!") else: print("错误:第二个数不是有效的整数!")else: print("错误:第一个数不是有效的整数!")这种写法能挡住一部分非法输入,但有明显缺点:
1. 代码臃肿:业务与防护逻辑缠在一起
2. 可读性差:层层嵌套,主流程被淹没
3. 维护成本高:每加一种校验就多一层分支
4. 很难堵全:例如 isdigit() 不接受负数 -3
结论:简单前置校验可以有,但系统性错误处理应交给异常机制。
5. 异常类的继承体系
Python 内置异常是一棵继承树。开发中最常接触的是 Exception 及其子类;KeyboardInterrupt、SystemExit 等直接挂在 BaseException 下,一般不要用裸 except: 去吞掉它们。
精简示意:
BaseException ├── KeyboardInterrupt ├── SystemExit ├── GeneratorExit └── Exception # 业务代码通常捕获这一支 ├── ArithmeticError │ └── ZeroDivisionError ├── LookupError │ ├── IndexError │ └── KeyError ├── OSError │ ├── FileNotFoundError │ └── PermissionError ├── TypeError ├── ValueError └── ...匹配规则:except 会捕获该类及其所有子类。因此要把更具体的异常写在前面,把更宽泛的(如 Exception)放在后面。
6. try-except 基础语法
try: 可能出错的代码except: 出错后的处理代码把求商改成异常处理版:
try: num1 = int(input("请录入第一个数:")) num2 = int(input("请录入第二个数:")) print(f"商:{num1 // num2}")except: print("程序出现异常")print("后续逻辑代码1")print("后续逻辑代码2")执行路径:
有异常:产生异常 → 找最近 except → 处理 → 继续往下跑无异常:跳过 except → 继续往下跑注意:裸 except: 会捕获几乎所有异常,生产代码应尽量写成具体类型,至少写成 except Exception:。
7. 捕获指定异常
多个 except自上而下匹配,命中后不再继续往下找:
try: num1 = int(input("请录入第一个数:")) num2 = int(input("请录入第二个数:")) print(f"商:{num1 // num2}")except ValueError: print("输入有误,请输入整数!")except ZeroDivisionError: print("除数不能为 0!")except Exception: print("意外异常")print("后续逻辑代码1") | | |
|---|
ValueError | int("abc") | |
ZeroDivisionError | n // 0 | |
Exception | | |
也可以用元组一次捕获多种类型:
except (ValueError, TypeError) as e: print(f"输入类型或格式错误:{e}")下篇继续讲:获取异常信息、traceback、else/finally、raise、自定义异常与异常传递。