Z
ZHANK
常用技能

异常处理

try/except/finally/else、raise from 链式异常、自定义异常类

异常处理

程序运行时难免出错。异常处理让你优雅地应对错误,而不是让程序崩溃。

学完本章你将掌握: try-except、捕获多种异常、else/finally、raise 抛出、自定义异常。


一、什么是异常

python
# 正常运行
num = int("42")
print(num * 2)  # 84

# 但是用户可能输入非数字
# num = int("abc")  # ❌ ValueError: invalid literal for int()...

不处理异常,整个程序崩溃。处理了,程序可以继续运行或优雅退出。


二、try-except 基础

python
try:
    num = int("abc")
    print(num)
except ValueError:
    print("无法将 'abc' 转为数字!")
    num = 0

print(f"使用默认值: {num}")  # 程序继续运行

三、捕获多种异常

python
def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        return "错误:不能除以零!"
    except TypeError:
        return "错误:类型不正确!"
    except Exception as e:
        return f"未知错误: {e}"
    else:
        return f"结果: {result}"

print(safe_divide(10, 2))    # 结果: 5.0
print(safe_divide(10, 0))    # 错误:不能除以零!
print(safe_divide("a", 2))   # 错误:类型不正确!

💡 捕获顺序很重要:把具体的异常(如 ZeroDivisionError)放在前面,通用的(如 Exception)放最后。如果顺序反了,具体异常永远不会被触发。


四、else 和 finally

python
# else:无异常时执行
try:
    num = int("42")
except ValueError:
    print("转换失败")
else:
    print(f"转换成功: {num * 2}")  # 只在无异常时运行

# finally:无论如何都执行(常用于清理资源)
try:
    f = open("data.txt", "r")
    content = f.read()
except FileNotFoundError:
    print("文件不存在")
finally:
    print("操作完成")  # 文件存在与否都会执行
    # 实际代码中这里应关闭文件

五、常见异常类型

异常触发条件
`ValueError`值不正确(如 `int("abc")`)
`TypeError`类型错误(如 `"a" + 1`)
`IndexError`列表索引越界
`KeyError`字典键不存在
`ZeroDivisionError`除以零
`FileNotFoundError`文件不存在
`AttributeError`对象没有该属性
`ImportError`导入模块失败
python
# 综合示例
def process_data(data, index):
    try:
        item = data[index]
        result = int(item)
        return 100 / result
    except IndexError:
        return "索引超出范围"
    except ValueError:
        return f"'{item}' 不是数字"
    except ZeroDivisionError:
        return "不能除以零"

六、raise——主动抛出异常

python
def set_age(age):
    if age < 0:
        raise ValueError("年龄不能为负数!")
    if age > 150:
        raise ValueError(f"年龄 {age} 不太合理!")
    print(f"年龄设置为 {age}")

set_age(25)       # 正常
# set_age(-5)     # ❌ ValueError: 年龄不能为负数!

七、自定义异常

python
class InvalidAgeError(ValueError):
    """自定义年龄异常"""
    pass

class UnderageError(Exception):
    """未成年人异常"""
    pass

def check_entry(age):
    if age < 0:
        raise InvalidAgeError("年龄无效")
    if age < 18:
        raise UnderageError("未成年人禁止入内")
    return "欢迎光临"

try:
    print(check_entry(15))
except UnderageError as e:
    print(e)  # 未成年人禁止入内

八、实战:安全的输入处理

python
def get_number(prompt):
    """反复要求用户输入,直到得到合法数字"""
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            print("请输入一个有效的整数!")

# age = get_number("请输入年龄: ")

# 安全读取文件
def safe_read(filename, default=""):
    try:
        with open(filename, "r", encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        return default
    except PermissionError:
        return f"没有权限读取 {filename}"

九、traceback——获取详细错误信息

python
import traceback

try:
    1 / 0
except ZeroDivisionError:
    print("出错了,详细信息:")
    traceback.print_exc()  # 打印完整调用栈

小结

  1. try-except 捕获异常,程序不会崩溃
  2. 按具体→通用的顺序捕获异常
  3. else 在无异常时执行,finally 无论如何都执行
  4. raise 主动抛出异常
  5. 可以继承 Exception 创建自定义异常

小练习

  1. 写一个 safe_int() 函数,反复要求输入直到得到合法整数
  2. 写一个函数打开文件,处理文件不存在和权限错误两种情况
  3. 创建一个自定义异常 InsufficientBalanceError 用于余额不足场景

💬 下一章预告:学完异常处理,下一章学习模块与包——组织和复用代码的最佳方式。