当前位置:首页>python>【从零开始学Python】篇9-基础语法章-异常处理

【从零开始学Python】篇9-基础语法章-异常处理

  • 2026-10-11 07:48:07
【从零开始学Python】篇9-基础语法章-异常处理

人非圣贤,孰能无过。程序也一样,关键是要学会“体面地犯错”

写程序的时候,你有没有遇到过这样的情况:

  • 用户输入了文字,程序却要转换成数字 → 💥 崩溃

  • 打开一个不存在的文件 → 💥 崩溃

  • 除以0 → 💥 崩溃

  • 列表索引超出范围 → 💥 崩溃

程序一崩溃,用户体验直接归零。

但其实,这些错误完全可以被优雅地处理——这就是异常处理的作用。


一、什么是异常?

异常 = 程序运行时发生的错误

当Python遇到无法继续执行的情况时,就会抛出一个异常。如果不处理,程序就会终止。

# 这几个都会引发异常int("hello")# ValueError10/0# ZeroDivisionErroropen("不存在.txt")# FileNotFoundError[1,2,3][10]# IndexError

常见异常类型(混个脸熟)

异常含义
NameError变量未定义
TypeError类型错误(如字符串+数字)
ValueError值错误(如int("abc"))
ZeroDivisionError除以0
IndexError索引超出范围
KeyError字典键不存在
FileNotFoundError文件不存在
AttributeError对象没有这个属性
ImportError导入模块失败

二、异常处理的基本语法

1️⃣ try-except —— 捕获异常

try:    num =int(input("请输入数字:"))    print(f"你输入了:{num}")except ValueError:    print("错误:请输入有效的数字!")

执行流程:

  • try里的代码正常 → 跳过except

  • try里的代码出错 → 跳到对应的except执行

2️⃣ try-except-else —— 没有异常时执行

try:    num =int(input("请输入数字:"))except ValueError:    print("输入错误!")else:    # 只有在没有异常时才执行    print(f"输入正确,数字是{num}")

3️⃣ try-except-finally —— 无论是否异常都执行

try:    f =open("data.txt","r")    data = f.read()except FileNotFoundError:    print("文件不存在")finally:# 无论上面有没有异常,都会执行    print("这里的代码一定会执行")# 适合用来关闭资源    if 'f' in locals():                f.close()

finally 通常用于清理工作(关闭文件、释放连接等)

4️⃣ 捕获多个异常

try:    num =int(input("请输入数字:"))    result =100/ numexcept ValueError:    print("请输入数字!")except ZeroDivisionError:    print("不能除以0!")

5️⃣ 合并多个异常

try:    num =int(input("请输入数字:"))    result =100/ numexcept(ValueError, ZeroDivisionError)as e:    print(f"输入错误:{e}")

6️⃣ 捕获所有异常(不推荐)

try:    risky_code()except Exception as e:    print(f"发生错误:{e}")

⚠️ 谨慎使用:捕获所有异常会隐藏你没预料到的bug。


三、手动抛出异常:raise

有时候,你想主动触发异常:

def set_age(age):    if age <0or age >150:        raise ValueError("年龄必须在0-150之间")    print(f"年龄设置为:{age}")try:        set_age(200)except ValueError as e:    print(f"出错了:{e}")

重新抛出异常

try:    do_something()except Exception as e:    # 记录日志        log_error(e)    # 重新抛出,让上层处理    raise

四、自定义异常

你可以定义自己的异常类型:

# 定义一个自定义异常class BalanceInsufficientError(Exception):      """余额不足异常"""      passdef withdraw(balance, amount):      if amount > balance:            raise BalanceInsufficientError(f"余额不足,需要{amount},只有{balance}")          balance -= amount       return balancetry:      new_balance = withdraw(100,200)except BalanceInsufficientError as e:   print(f"取款失败:{e}")

自定义异常让你的代码

意图更清晰,别人一看异常名就知道发生了什么。


五、assert 断言 —— 调试利器

断言:假设某个条件一定成立,如果不成立就抛出 AssertionError。

def divide(a, b):    assert b !=0,"除数不能为0"    return a / bdivide(10,0)# AssertionError: 除数不能为0

断言主要用于

开发阶段检测逻辑错误,生产环境可以用 -O 参数关闭。


六、实战案例

案例1:安全的整数输入函数

def safe_int_input(prompt, retry=3):    """安全的整数输入函数,带重试机制"""    for i inrange(retry):        try:              value =int(input(prompt))              return value          except ValueError:          print(f"输入无效,还剩{retry - i -1}次机会")          raise ValueError(f"连续{retry}次输入错误")# 使用age = safe_int_input("请输入年龄:")print(f"年龄:{age}")

案例2:安全读取文件

def safe_read_file(filename, default=""):    """安全读取文件,文件不存在时返回默认值"""    try:        with open(filename,"r", encoding="utf-8")as f:        return f.read()    except FileNotFoundError:        print(f"文件 {filename} 不存在,返回默认值")        return default     except PermissionError:print(f"没有权限读取 {filename}")        return default    except Exception as e:        print(f"读取文件时发生未知错误:{e}")        return default content = safe_read_file("config.txt","{}")

案例3:带重试的网络请求模拟

import time import randomdef fetch_data(url):    """模拟网络请求,随机失败"""    if random.random()<0.7:      # 70%概率失败      raise ConnectionError("网络连接失败")    return f"从 {url} 获取的数据"def fetch_with_retry(url, max_retries=3, delay=1):    """带重试的数据获取函数"""    for attempt inrange(max_retries):        try:            print(f"尝试第 {attempt +1} 次...")                        data = fetch_data(url)            print("成功!")            return data        except ConnectionError as e:            print(f"失败:{e}")        if attempt < max_retries -1:            print(f"等待 {delay} 秒后重试...")                            time.sleep(delay)        else:            print("所有重试均失败")            raise# 测试try:        result = fetch_with_retry("https://api.example.com/data")except ConnectionError:    print("获取数据失败,请稍后再试")

案例4:计算器(完整异常处理)

def calculator():    """带完整异常处理的计算器"""    print("=== 简单计算器 ===")    print("支持运算:+ - * /")    while True:        try:            # 获取输入                        exp =input("\n请输入表达式(如 3 + 5)或输入 q 退出:")            if exp.lower()=='q':                print("再见!")                break            # 解析表达式                        parts = exp.split()            if len(parts)!=3:                print("格式错误,请使用:数字 运算符 数字")                continue                        a, op, b = parts                        a =float(a)                        b =float(b)            # 执行运算            if op =='+':                result = a + b            elif op =='-':                                result = a - b            elif op =='*':                                result = a * b            elif op =='/':                if b ==0:                    raise ZeroDivisionError("不能除以0")                                result = a / b            else:                print(f"不支持的运算符:{op}")                continue            print(f"结果:{result}")        except ValueError:            print("错误:请输入有效的数字")        except ZeroDivisionError as e:            print(f"错误:{e}")        except KeyboardInterrupt:            print("\n用户中断")break        except Exception as e:            print(f"未知错误:{e}")calculator()

案例5:银行取款系统

class InsufficientBalanceError(Exception):"""自定义:余额不足异常"""pass class AccountNotFoundError(Exception):"""自定义:账户不存在异常"""pass class BankAccount:    def __init__(self, account_id, balance=0):        self.account_id = account_id                self.balance = balance    def withdraw(self, amount):        if amount <=0:           raise ValueError("取款金额必须大于0")        if amount > self.balance:           raise InsufficientBalanceError(f"余额不足!当前余额:{self.balance},需要:{amount}")                self.balance -= amount return self.balance     def deposit(self, amount):         if amount <=0:           raise ValueError("存款金额必须大于0")                 self.balance += amount return self.balance# 使用示例accounts ={"1001": BankAccount("1001",1000),"1002": BankAccount("1002",500),}def bank_system():     print("=== 银行系统 ===")     try:                 acc_id =input("请输入账号:")         if acc_id not in accounts:           raise AccountNotFoundError(f"账号 {acc_id} 不存在")                 account = accounts[acc_id]                 action =input("请选择操作(1=存款,2=取款):")                 amount =float(input("请输入金额:"))         if action =='1':                       new_balance = account.deposit(amount)           print(f"存款成功!当前余额:{new_balance}")         elif action =='2':                       new_balance = account.withdraw(amount)           print(f"取款成功!当前余额:{new_balance}")         else:print("无效操作")       except AccountNotFoundError as e:           print(f"账户错误:{e}")       except InsufficientBalanceError as e:           print(f"取款失败:{e}")       except ValueError as e:           print(f"金额错误:{e}")       except Exception as e:           print(f"系统错误:{e}")bank_system()

七、最佳实践与避坑指南

✅ 好的做法

# 1. 捕获具体异常,而不是全部try:    result =int(text)except ValueError:    # 具体    pass# 2. 异常信息要清晰raise ValueError(f"年龄不能为负数:{age}")# 3. 用 finally 释放资源f =open("file.txt")try:    process(f)finally:        f.close()# 4. 用 with 自动管理(更推荐)with open("file.txt")as f:    process(f)# 5. 记录异常日志import loggingtry:    risky_code()except Exception as e:    logging.exception("发生异常")

❌ 坏的做法

# 1. 捕获所有异常(隐藏bug)try:    data = process()except:    # 连键盘中断都被吞了    pass# 2. 异常太宽泛except Exception as e:# 大多数情况没问题,但应该更具体    pass# 3. 什么都不做的 excepttry:    x =int(text)except:    pass# 静默失败,问题很难排查# 4. 抛出太通用的异常raise Exception("出错了")# 用更具体的异常类型

🔥 常见坑点

# 坑1: except 顺序try:    value =int("abc")except Exception:# 这个会捕获所有,下面的 ValueError 永远不会执行    print("通用错误")except ValueError:# 永远到不了这里    print("值错误")# ✅ 正确:先具体后通用try:    value =int("abc")except ValueError:    print("值错误")except Exception:    print("其他错误")# 坑2:不知道异常类型# 可以这样打印出来try:        risky_code()except Exception as e:    print(type(e).__name__, e)# 看看是什么异常

八、异常处理速查表

场景写法
捕获特定异常except ValueError:
捕获多个异常except (ValueError, TypeError):
获取异常对象except ValueError as e:
没有异常时执行else:
无论是否异常都执行finally:
手动抛出异常raise ValueError("信息")
自定义异常class MyError(Exception): pass
断言assert condition, "错误信息"

写在最后

异常处理不是“逃避错误”,而是让程序在面对意外情况时,能够体面地处理。

记住这几个原则:

1️⃣ 预见可能出错的地方,提前用 try 保护
2️⃣ 捕获具体的异常,不要一股脑全抓
3️⃣ 异常信息要清晰,方便排查问题
4️⃣ finally 或 with 确保资源释放
5️⃣ 不要静默吞掉异常,至少打个日志


📌 如果觉得有用,点赞+在看+转发 给正在学Python的小伙伴!

优雅地犯错,体面地恢复。我们下期见! 👋

最新文章

随机文章