跳到主要内容

异常处理机制

让程序优雅地处理错误

异常概述

什么是异常

异常 是程序运行时发生的错误,会中断程序正常执行。

# 常见异常
print(10 / 0) # ZeroDivisionError
print(int("abc")) # ValueError
print(undefined_var) # NameError
print([1,2,3][10]) # IndexError

为什么需要异常处理

  1. 程序健壮性:不因意外错误崩溃
  2. 用户体验:给用户友好的错误提示
  3. 调试方便:明确知道哪里出错
  4. 资源清理:确保资源正确释放

异常处理语法

try-except

try:
# 可能出错的代码
num = int(input("请输入数字:"))
result = 10 / num
print(f"结果:{result}")
except:
# 发生异常时执行
print("发生错误!")

捕获特定异常

try:
num = int(input("请输入数字:"))
result = 10 / num
print(f"结果:{result}")
except ValueError:
print("请输入有效的数字!")
except ZeroDivisionError:
print("除数不能为0!")

捕获多个异常

try:
# 代码
pass
except (ValueError, TypeError):
print("值错误或类型错误")

# 或分开处理
try:
pass
except ValueError:
print("值错误")
except TypeError:
print("类型错误")

获取异常信息

try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"错误类型:{type(e).__name__}")
print(f"错误信息:{e}")

完整语法

try-except-else-finally

try:
# 可能出错的代码
num = int(input("请输入数字:"))
result = 10 / num
except ValueError:
# 发生ValueError时执行
print("请输入有效数字")
except ZeroDivisionError:
# 发生ZeroDivisionError时执行
print("除数不能为0")
else:
# 没有异常时执行
print(f"结果:{result}")
finally:
# 无论是否异常都执行
print("程序结束")

finally的作用

# 确保资源释放
def read_file(filename):
file = None
try:
file = open(filename, 'r')
content = file.read()
return content
except FileNotFoundError:
print("文件不存在")
return None
finally:
if file:
file.close() # 确保文件关闭
print("文件已关闭")

常见异常类型

异常类型说明示例
SyntaxError语法错误if True print()
NameError变量未定义print(undefined)
TypeError类型错误"2" + 2
ValueError值错误int("abc")
IndexError索引越界[1,2,3][10]
KeyError键不存在{"a":1}["b"]
ZeroDivisionError除零错误10 / 0
FileNotFoundError文件不存在open("不存在.txt")
AttributeError属性错误"hello".foo()
ImportError导入错误import 不存在的模块

异常层级

# 所有异常都继承自BaseException
# 常用异常继承自Exception

BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── StopIteration
├── ArithmeticError
│ └── ZeroDivisionError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── ValueError
├── TypeError
└── ...

抛出异常

raise语句

def set_age(age):
if age < 0:
raise ValueError("年龄不能为负数")
if age > 150:
raise ValueError("年龄不能超过150")
return age

try:
set_age(-5)
except ValueError as e:
print(f"错误:{e}")

重新抛出异常

def process():
try:
result = 10 / 0
except ZeroDivisionError:
print("记录日志...")
raise # 重新抛出当前异常

异常链

try:
result = 10 / 0
except ZeroDivisionError as e:
raise ValueError("计算失败") from e

自定义异常

# 自定义异常类
class AgeError(Exception):
"""年龄相关异常"""
def __init__(self, age, message="年龄无效"):
self.age = age
self.message = message
super().__init__(self.message)

def __str__(self):
return f"{self.message}: {self.age}"


def set_age(age):
if age < 0 or age > 150:
raise AgeError(age, "年龄必须在0-150之间")
return age


try:
set_age(-5)
except AgeError as e:
print(e) # 年龄必须在0-150之间: -5

断言 assert

# 断言:条件为False时抛出AssertionError
def divide(a, b):
assert b != 0, "除数不能为0"
return a / b

# 调试时使用
def process_data(data):
assert isinstance(data, list), "data必须是列表"
assert len(data) > 0, "列表不能为空"
# 处理数据...

上下文管理器 with

# 传统方式
file = open("test.txt", "r")
try:
content = file.read()
finally:
file.close()

# 使用with(推荐)
with open("test.txt", "r") as file:
content = file.read()
# 自动关闭文件

# 多个上下文管理器
with open("input.txt") as f_in, open("output.txt", "w") as f_out:
content = f_in.read()
f_out.write(content.upper())

最佳实践

1. 只捕获预期的异常

# 不好:捕获所有异常
try:
do_something()
except:
pass # 吞掉所有错误

# 好:捕获特定异常
try:
do_something()
except ValueError:
handle_value_error()

2. 异常信息要有意义

# 不好
raise Exception("错误")

# 好
raise ValueError(f"年龄 {age} 无效,必须在 0-150 之间")

3. 不要用异常控制流程

# 不好:用异常判断
try:
value = my_dict["key"]
except KeyError:
value = default

# 好:用get方法
value = my_dict.get("key", default)

4. 记录异常信息

import logging

try:
risky_operation()
except Exception as e:
logging.error(f"操作失败: {e}", exc_info=True)
raise

实战示例

示例:安全的用户输入

def get_integer(prompt, min_val=None, max_val=None):
"""安全获取整数输入"""
while True:
try:
value = int(input(prompt))
if min_val is not None and value < min_val:
print(f"值不能小于 {min_val}")
continue
if max_val is not None and value > max_val:
print(f"值不能大于 {max_val}")
continue
return value
except ValueError:
print("请输入有效的整数")


age = get_integer("请输入年龄:", 0, 150)
print(f"年龄:{age}")

示例:文件处理

def read_json_file(filepath):
"""安全读取JSON文件"""
import json

try:
with open(filepath, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f"文件不存在:{filepath}")
return None
except json.JSONDecodeError as e:
print(f"JSON格式错误:{e}")
return None
except Exception as e:
print(f"读取失败:{e}")
return None

练习

练习1:安全除法

# 编写函数,安全地进行除法运算
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
except TypeError:
return None

print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # None
print(safe_divide("10", 2)) # None

本章小结

  1. try-except:捕获和处理异常
  2. finally:确保代码执行(清理资源)
  3. raise:主动抛出异常
  4. 自定义异常:创建业务相关的异常类
  5. with语句:上下文管理器,自动资源管理

下一步

学会了异常处理,下一章学习文件操作。

→ 继续阅读:23-文件操作详解