🧩 第四章 面向对象编程技术:去除冗余、进行整理
📄 一句话概括

返回码每层都要判、漏一层就丢;异常中间两层一个字没写,错误直接穿到最外层。

📝 本节和前面几节的关系

4.15 管名字的边界,这一节管失败的边界。

一、返回码时代的麻烦

每层都要写 if err != OK: return err——写十层就写十遍。 漏一层,错误就被吞掉,程序带着坏数据继续跑。 更糟的是它把正常流程和错误处理搅在一起:读代码时分不清哪句是主逻辑。

二、实测穿透

level1 → level2 → level3,第三层 raise,最外层 try/except。 实测:最外层接住【第三层出错】,中间两层一个字都没写。 这就是异常和返回码的根本差别:错误能【跳】,不用一层层传。

三、它把两件事分开了

正常流程和错误处理被分到两个地方——这是【边界】这个词的确切含义。 主逻辑读起来是连续的,错误处理集中在能处理它的那一层。 所以异常不只是语法糖,它是一种结构:它规定了【谁负责收拾】。

四、代价

可能被吞:except: pass 会把错误彻底埋掉,比返回码漏判更难查。 开销略大:抛异常要收集栈信息,比 return 贵(虽然只在出错路径上)。 所以捕获要具体:不要一把抓 Exception,更不要空 except。 异常管的是【意外的失败】,不是【正常的分支】。拿它做流程控制,等于把边界又搅乱了。

五、动手 Demo:进化的 OOP 结构之二——异常

三层调用,最里面抛异常,中间两层什么都不写。 看错误能不能直接穿到最外层。

def level3():
    raise ValueError("第三层出错")

def level2():
    level3()          # 不判、不传

def level1():
    level2()          # 不判、不传

try:
    level1()
except ValueError as e:
    print("在最外层接住:", e)

实跑输出:

--------------------------------------------------------------------
进化的 OOP 结构之二——异常
--------------------------------------------------------------------

  没有异常的时代,错误靠**返回码**一层层往上传——**每层都要判,漏一层就丢。**
  有异常之后,**错误可以【跳】到能处理它的那一层。**

      level1 → level2 → level3(抛异常)

      在最外层接住:**第三层出错**

  实测:**中间两层一个字都没写,异常直接穿过去了。**
  这在返回码时代是不可能的——**每层都得写 `if err != OK: return err`。**

                           返回码         异常
  ---------------------------------------------------------
  每层要写吗               要             不用
  漏判会怎样               错误被吞掉     不会,会一直冒泡
  正常逻辑会不会被搅乱     会             不会

  **注意最后一行**:返回码把【正常流程】和【错误处理】搅在一起,
  **异常把它们分开了**——**这正是【边界】的意思。**

实测:中间两层一个字都没写,异常直接穿到最外层。 它把正常流程和错误处理分开了——这正是【边界】的意思;代价是可能被吞,所以捕获要具体。

30 秒实验

  1. 找一处空 except,判断它到底吞掉了什么。
  2. 把一段用返回码传错误的代码改成异常,看少写了几行。

常见误区

  1. 以为异常只是【另一种返回错误的方式】。它让错误能跳层。
  2. 以为捕获越多越安全。空 except 会把错误彻底埋掉。
自测三问(点开对答案) 1. 异常比返回码强在哪? —— 错误能跳层:中间层不用判、不用传,直接穿到能处理它的那一层——实测中间两层一个字都没写。 2. 异常把哪两件事分开了? —— 正常流程和错误处理——主逻辑连续,错误处理集中在能收拾的那一层。 3. 异常有什么代价? —— 可能被吞(空 except 比漏判更难查)、抛时要收集栈信息开销略大,所以捕获要具体。

相关笔记