python系列之异常处理:给代码穿上“防弹衣”

不为失败找理由,只为成功找方法。所有的不甘,因为还心存梦想,所以在你放弃之前,好好拼一把,只怕心老,不怕路长。

python系列前期章节

  1. python系列之注释与变量
  2. python系列之输入输出语句与数据类型
  3. python系列之运算符
  4. python系列之控制流程语句
  5. python系列之字符串
  6. python系列之列表
  7. python系列之元组
  8. python系列之字典
  9. python系列之集合
  10. python系列之函数基础
  11. python系列之函数进阶
  12. python系统之综合案例1:用python打造智能诗词生成助手
  13. python系列之综合案例2:用python开发《魔法学院入学考试》文字冒险游戏
  14. python系列之类与对象:面向对象编程(用造人计划秒懂面向对象)
  15. python系列之类与对象:python系列之详解面向对象的属性)
  16. python系列之详解面向对象的函数
  17. 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()

十二、总结:异常处理的核心原则

  1. 预见性:预测可能出错的地方
  2. 具体性:捕获具体的异常类型
  3. 优雅性:给用户友好的错误信息
  4. 完整性:使用finally确保资源清理
  5. 记录性:记录异常信息以便调试

十三、练习题

  1. 修改上面的用户管理系统,添加删除用户功能并处理相关异常
  2. 创建一个计算器程序,处理所有可能的输入错误
  3. 编写一个文件复制程序,处理文件不存在、权限不足等异常
  4. 为网络请求程序添加重试机制(遇到异常时重试3次)

记住:好的异常处理不是让程序永远不出错,而是让程序在出错时能够优雅地处理问题,提供有用的信息,并继续运行或安全退出。现在,去给你的代码穿上"防弹衣"吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值