不为失败找理由,只为成功找方法。所有的不甘,因为还心存梦想,所以在你放弃之前,好好拼一把,只怕心老,不怕路长。
python系列之异常处理:给代码穿上“防弹衣”
python系列前期章节
- python系列之注释与变量
- python系列之输入输出语句与数据类型
- python系列之运算符
- python系列之控制流程语句
- python系列之字符串
- python系列之列表
- python系列之元组
- python系列之字典
- python系列之集合
- python系列之函数基础
- python系列之函数进阶
- python系统之综合案例1:用python打造智能诗词生成助手
- python系列之综合案例2:用python开发《魔法学院入学考试》文字冒险游戏
- python系列之类与对象:面向对象编程(用造人计划秒懂面向对象)
- python系列之类与对象:python系列之详解面向对象的属性)
- python系列之详解面向对象的函数
- python系列之面向对象的三大特性
阅读建议:想象自己是一名代码医生,正在学习如何诊断和治疗程序的各种"疾病"!🚑
一、前言:为什么需要异常处理?
想象一下:你正在开车,突然前方出现一块大石头。
没有异常处理:直接撞上去,车毁人亡,程序崩溃!
有异常处理:发现石头→绕过去或者刹车→继续安全行驶。
异常处理就是程序的"安全气囊",让程序在遇到问题时不会直接崩溃,而是优雅地处理问题。
二、什么是异常?程序的"感冒发烧"
异常就是程序运行时发生的错误事件。就像人会感冒发烧一样,程序运行中也会出现各种"不适"。
# 常见的异常例子
print(10 / 0) # ZeroDivisionError: 除零错误
print(undefined_var) # NameError: 未定义变量
numbers = [1, 2, 3]
print(numbers[10]) # IndexError: 索引越界
三、异常处理的基本语法:try-except
3.1 最简单的异常处理
try:
# 可能出错的代码
number = int(input("请输入一个数字: "))
result = 10 / number
print(f"结果是: {result}")
except:
# 如果出错,执行这里的代码
print("哎呀,出错了!请检查输入是否正确")
3.2 捕获特定类型的异常
try:
number = int(input("请输入一个数字: "))
result = 10 / number
print(f"结果是: {result}")
except ValueError:
print("输入的不是有效数字!")
except ZeroDivisionError:
print("不能除以零!")
except Exception as e:
print(f"发生了未知错误: {e}")
四、异常处理的完整结构:try-except-else-finally
def divide_numbers():
try:
# 尝试执行的代码
numerator = int(input("请输入分子: "))
denominator = int(input("请输入分母: "))
result = numerator / denominator
except ValueError:
# 处理值错误
print("请输入有效的数字!")
return None
except ZeroDivisionError:
# 处理除零错误
print("分母不能为零!")
return None
except Exception as e:
# 处理其他所有异常
print(f"发生了未知错误: {e}")
return None
else:
# 如果没有发生异常,执行这里的代码
print("计算成功!")
return result
finally:
# 无论是否发生异常,都会执行的代码
print("计算过程结束")
# 测试函数
result = divide_numbers()
if result is not None:
print(f"最终结果: {result}")
各部分的作用:
try:尝试执行可能出错的代码except:捕获并处理特定异常else:没有异常时执行的代码finally:无论是否异常都会执行的代码(常用于清理资源)
五、常见的异常类型
Python有很多内置的异常类型,就像医院有不同科室一样:
| 异常类型 | 描述 | 例子 |
|---|---|---|
ValueError | 值错误 | int("abc") |
TypeError | 类型错误 | "hello" + 123 |
IndexError | 索引错误 | [1,2,3][10] |
KeyError | 键错误 | {"a":1}["b"] |
FileNotFoundError | 文件未找到 | open("不存在.txt") |
ZeroDivisionError | 除零错误 | 10 / 0 |
ImportError | 导入错误 | import 不存在的模块 |
六、主动抛出异常:raise语句
有时候我们需要主动"制造"异常,就像医生主动给病人做检查一样。
def calculate_age(birth_year):
if birth_year < 1900 or birth_year > 2023:
# 主动抛出异常
raise ValueError("出生年份必须在1900-2023之间")
return 2023 - birth_year
try:
age = calculate_age(1800)
print(f"年龄是: {age}")
except ValueError as e:
print(f"输入错误: {e}")
七、自定义异常:创建自己的异常类型
你可以创建特定的异常类型,就像医院有专科医生一样。
# 自定义异常类
class TooYoungError(Exception):
"""年龄太小异常"""
def __init__(self, age, message="年龄未满18岁"):
self.age = age
self.message = message
super().__init__(self.message)
class TooOldError(Exception):
"""年龄太大异常"""
pass
def check_driving_age(age):
if age < 18:
raise TooYoungError(age)
elif age > 80:
raise TooOldError("年龄太大,不建议驾驶")
else:
return "可以驾驶"
# 测试自定义异常
ages = [16, 25, 85]
for age in ages:
try:
result = check_driving_age(age)
print(f"{age}岁: {result}")
except TooYoungError as e:
print(f"{age}岁: {e},还差{18-age}年才能驾驶")
except TooOldError as e:
print(f"{age}岁: {e}")
except Exception as e:
print(f"{age}岁: 发生未知错误 - {e}")
八、实际应用场景
8.1 文件操作中的异常处理
def read_file_safely(filename):
try:
with open(filename, 'r', encoding='utf-8') as file:
content = file.read()
print("文件内容:")
print(content)
return content
except FileNotFoundError:
print(f"错误: 找不到文件 {filename}")
except PermissionError:
print(f"错误: 没有权限读取文件 {filename}")
except UnicodeDecodeError:
print("错误: 文件编码问题")
except Exception as e:
print(f"读取文件时发生未知错误: {e}")
finally:
print("文件读取操作完成")
# 测试文件读取
read_file_safely("example.txt")
8.2 网络请求中的异常处理
import requests
def safe_web_request(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # 如果HTTP状态码不是200,抛出异常
print("请求成功!")
return response.text
except requests.exceptions.Timeout:
print("错误: 请求超时")
except requests.exceptions.ConnectionError:
print("错误: 网络连接问题")
except requests.exceptions.HTTPError as e:
print(f"HTTP错误: {e}")
except Exception as e:
print(f"网络请求失败: {e}")
return None
# 测试网络请求
safe_web_request("https://httpbin.org/status/404")
8.3 数据库操作中的异常处理
import sqlite3
def database_operations():
conn = None
try:
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# 创建表
cursor.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')
# 插入数据
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)",
("张三", 25))
conn.commit()
print("数据库操作成功!")
except sqlite3.Error as e:
print(f"数据库错误: {e}")
if conn:
conn.rollback()
except Exception as e:
print(f"未知错误: {e}")
finally:
# 确保连接被关闭
if conn:
conn.close()
print("数据库连接已关闭")
database_operations()
九、异常处理的最佳实践
9.1 不要过度使用try-except
# 不好的做法:过度捕获异常
try:
value = my_dict[key]
except KeyError:
value = default_value
# 好的做法:使用get方法
value = my_dict.get(key, default_value)
9.2 具体的异常比通用的更好
# 不好的做法:捕获所有异常
try:
# 一些代码
except:
pass
# 好的做法:捕获具体异常
try:
# 一些代码
except (ValueError, TypeError) as e:
print(f"特定错误: {e}")
9.3 记录异常信息
import logging
# 配置日志
logging.basicConfig(level=logging.ERROR)
try:
result = 10 / 0
except ZeroDivisionError as e:
logging.error(f"除零错误发生: {e}", exc_info=True)
9.4 创建上下文管理器
class SafeFileOpener:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
try:
self.file = open(self.filename, self.mode, encoding='utf-8')
return self.file
except Exception as e:
print(f"无法打开文件: {e}")
raise
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
if exc_type:
print(f"发生错误: {exc_val}")
return True # 抑制异常传播
# 使用上下文管理器
with SafeFileOpener("test.txt", "r") as file:
content = file.read()
print(content)
十、调试技巧:如何找到异常根源
10.1 使用traceback模块
import traceback
try:
result = 10 / 0
except Exception as e:
print("错误信息:", str(e))
print("详细的错误跟踪:")
traceback.print_exc()
10.2 使用logging记录完整信息
import logging
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
try:
risky_operation()
except Exception as e:
logging.exception("操作失败")
十一、综合实战:健壮的应用程序
让我们创建一个完整的、带有异常处理的应用程序:
import json
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
class UserManager:
def __init__(self, filename="users.json"):
self.filename = filename
self.users = self.load_users()
def load_users(self):
"""加载用户数据"""
try:
with open(self.filename, 'r', encoding='utf-8') as file:
return json.load(file)
except FileNotFoundError:
logging.warning("用户文件不存在,创建新文件")
return []
except json.JSONDecodeError:
logging.error("用户文件格式错误")
return []
except Exception as e:
logging.error(f"加载用户数据失败: {e}")
return []
def save_users(self):
"""保存用户数据"""
try:
with open(self.filename, 'w', encoding='utf-8') as file:
json.dump(self.users, file, indent=2, ensure_ascii=False)
logging.info("用户数据保存成功")
except Exception as e:
logging.error(f"保存用户数据失败: {e}")
def add_user(self, name, age, email):
"""添加用户"""
try:
# 验证输入
if not all([name, age, email]):
raise ValueError("所有字段都必须填写")
if not isinstance(age, int) or age <= 0:
raise ValueError("年龄必须是正整数")
if "@" not in email:
raise ValueError("邮箱格式不正确")
# 添加用户
user = {
"id": len(self.users) + 1,
"name": name,
"age": age,
"email": email,
"created_at": datetime.now().isoformat()
}
self.users.append(user)
self.save_users()
logging.info(f"用户 {name} 添加成功")
return True
except ValueError as e:
logging.warning(f"输入验证失败: {e}")
return False
except Exception as e:
logging.error(f"添加用户失败: {e}")
return False
def get_user(self, user_id):
"""获取用户信息"""
try:
for user in self.users:
if user["id"] == user_id:
return user
raise ValueError(f"用户ID {user_id} 不存在")
except ValueError as e:
logging.warning(str(e))
return None
except Exception as e:
logging.error(f"获取用户信息失败: {e}")
return None
# 使用用户管理器
def main():
manager = UserManager()
while True:
print("\n=== 用户管理系统 ===")
print("1. 添加用户")
print("2. 查看用户")
print("3. 退出")
try:
choice = input("请选择操作 (1-3): ")
if choice == "1":
name = input("姓名: ")
age = int(input("年龄: "))
email = input("邮箱: ")
if manager.add_user(name, age, email):
print("用户添加成功!")
else:
print("用户添加失败,请检查输入")
elif choice == "2":
try:
user_id = int(input("请输入用户ID: "))
user = manager.get_user(user_id)
if user:
print(f"用户信息: {user}")
else:
print("用户不存在")
except ValueError:
print("请输入有效的数字ID")
elif choice == "3":
print("再见!")
break
else:
print("无效的选择,请重新输入")
except KeyboardInterrupt:
print("\n程序被用户中断")
break
except Exception as e:
logging.error(f"程序发生未知错误: {e}")
print("发生未知错误,请查看日志文件")
if __name__ == "__main__":
main()
十二、总结:异常处理的核心原则
- 预见性:预测可能出错的地方
- 具体性:捕获具体的异常类型
- 优雅性:给用户友好的错误信息
- 完整性:使用finally确保资源清理
- 记录性:记录异常信息以便调试
十三、练习题
- 修改上面的用户管理系统,添加删除用户功能并处理相关异常
- 创建一个计算器程序,处理所有可能的输入错误
- 编写一个文件复制程序,处理文件不存在、权限不足等异常
- 为网络请求程序添加重试机制(遇到异常时重试3次)
记住:好的异常处理不是让程序永远不出错,而是让程序在出错时能够优雅地处理问题,提供有用的信息,并继续运行或安全退出。现在,去给你的代码穿上"防弹衣"吧!
786

被折叠的 条评论
为什么被折叠?



