🧩 第五章 理解内存结构:程序员的基本素养
📄 一句话概括

过程式一 dump 就清清楚楚;OOP 的 dump 里全是对象,一行 repr 只给类名和地址,摊开要 3 行。

📝 本节和前面几节的关系

5.13 收完内存这一章,这个专栏讲当年调试这些对象有多费劲。

一、当年的处境

没有图形调试器,调试就是打印内存(dump)——把一块内存按格式摊出来看。 过程式程序一摊就是全部:全局变量、局部变量,一行一行看下去。 OOP 程序一摊全是对象:每个对象只给你一个地址,要看内容还得自己走进去。

二、实测差别

一行 repr:只给出类名和地址——User object at 0x...,一个字段都没告诉你。 要看清内容得自己递归:这个 2 层对象摊开是 3 行(name、addr、addr.city)。 层级越深,行数涨得越快——三层就是五、六行,还得自己写递归。 而全局变量一行 print(globals()) 就够了。

三、为什么费劲

数据不再平铺,而是挂在对象里,一层套一层。 这正是 5.6 说的【内存用法】的副作用:数据搬进对象之后,访问路径变长了。 所以这不是 OOP 的缺陷,是它的代价——换来的是数据有边界、有主人。

四、解法与教训

解法不是退回过程式,而是造工具:今天的调试器可以点开任意一层、可以搜索引用链。 还有一条更实用的:给类写 __repr__,让打印出来的东西有内容。 记住这一条:结构越深,越需要工具;靠眼睛看是看不动的。

五、动手 Demo:OOP 中 dump 看起来很费劲?

一个两层的对象,一行 repr 能看出什么。 再自己写个递归,看要几行才能看清。

u0 = User("甲", Addr("宜昌"))
print(repr(u0))          # 只有类名和地址

def dump(o, lvl=0, out=None):
    out = [] if out is None else out
    for k in sorted(vars(o)):
        v = getattr(o, k)
        if hasattr(v, "__dict__"):
            out.append("  " * lvl + k + ":")
            dump(v, lvl + 1, out)
        else:
            out.append("  " * lvl + k + " = " + str(v))
    return out

实跑输出:

--------------------------------------------------------------------
OOP 中 dump 看起来很费劲?
--------------------------------------------------------------------

  专栏【编程往事】说的是当年调试的事:**过程式程序一 dump 就清清楚楚,**
  **OOP 程序的 dump 里全是对象,人和对象之间还隔着一层。**实测这个差别:

      一行 repr 能看到什么:
        User object at 0x...
        (除了类名和地址,一个字段都没告诉你)

      要看全得自己递归,实测逐层摊开:

        addr:
          city = 宜昌
        name = 甲

      这个对象摊开要几行          3 行
      层级                       2 层(User → Addr)
      而全局变量一行就够          print(globals())

  **这就是当年觉得【费劲】的原因:数据不再平铺,而是挂在对象里,一层套一层。**
  **解法不是退回过程式,而是造工具**——今天的调试器可以点开任意一层。
  **记住这一条:结构越深,越需要工具;靠眼睛看是看不动的。**

实测:一行 repr 只给类名和地址,一个字段都不给;自己递归摊开要 3 行(2 层)。 数据不再平铺而是挂在对象里——这就是当年觉得费劲的原因,解法是造工具而不是退回过程式。

30 秒实验

  1. 给你自己的类写一个 __repr__,看调试时省多少事。
  2. 想一想:你的项目里最深的那个对象有几层。

常见误区

  1. 以为 dump 费劲说明 OOP 不好。那是代价,换来的是数据有边界。
  2. 以为今天的调试器就不用懂结构。不懂结构,连断点该打在哪儿都不知道。
自测三问(点开对答案) 1. 当年 dump OOP 程序为什么费劲? —— 数据不再平铺而是挂在对象里,一层套一层;一行 repr 只给类名和地址,要看内容得自己递归。 2. 实测这个两层对象摊开要几行? —— 3 行:name = 甲、addr:、city = 宜昌;而全局变量一行 print(globals()) 就够。 3. 现在的解法是什么? —— 造工具:调试器可以点开任意一层、搜索引用链;另外给自己的类写 __repr__ 也很实用。

相关笔记