📄 一句话概括
同一份数据,放全局区只有 1 份(2 个键),放堆上有 3 份——OOP 的特征就体现在这次搬家里。
📝 本节和前面几节的关系
5.5 分完三块地,这一节说 OOP 到底把东西放在了哪一块。
一、过程式放哪
数据放在模块级(静态区):一本全局字典,或者几个全局变量。 任何函数拿到名字就能改——不需要经过任何口子。 只有一份。想为第二个购物车单独存数据?没有地方放。
二、OOP 放哪
数据放在堆上的实例里:self.total、self.items。
要改只能走方法——c.add(...),绕不过去。
要几份就造几个实例。实测三个购物车各自记着自己的 total:5/8/3。
三、实测对比
全局版:共享数据里 2 个键(items/total),1 份数据。 OOP 版:模块级只有 3 个对象名,3 份独立数据。 名字的数量差不多,但能存的数据份数差三倍——这就是内存用法的差别。
四、这才是 OOP 的特征
OOP 的特征不是【有类】,是数据换了地方:从一块全局区搬进 N 块堆对象。 搬完之后【谁能改它】变了:从【所有人】变成【拿到这个对象的人】。 所以 3.9 那 480 条依赖边是被【搬家】断掉的,不是被语法断掉的。 判断一个程序是不是真 OOP,看它的数据住哪,别只看它有没有 class。
五、动手 Demo:OOP 的特征在于内存的用法
同一套购物车逻辑:一遍用一本全局字典,一遍用三个实例。 数一数各自能存几份独立数据。
GBL = {"total": 0, "items": []} # 过程式:一本共享数据
def add_global(name, price):
GBL["total"] += price
GBL["items"].append(name)
class Cart:
def __init__(self):
self.total = 0
self.items = []
def add(self, name, price):
self.total += price
self.items.append(name)
c1, c2, c3 = Cart(), Cart(), Cart()
实跑输出:
--------------------------------------------------------------------
OOP 的特征在于内存的用法
--------------------------------------------------------------------
过程式把数据放在模块级(静态区),**任何函数都能直接改。**
OOP 把数据放进实例(堆),**要改只能走对象。**实测量一量:
全局版:共享数据里几个键 2 个(items/total)
全局版:能存几份独立数据 1 份(就这么一本)
OOP 版:模块级只有对象名 3 个(c1/c2/c3)
OOP 版:能存几份独立数据 3 份
三个购物车的 total 5/8/3
全局版的 total 只有一个 5
每个实例自己的字段 items/total
**这才是【OOP 的特征在于内存用法】的确切含义:**
**数据从【一块全局区】搬进【N 块堆上的对象】,**
**于是【谁能改它】从【所有人】变成【拿到这个对象的人】。**
实测:全局版共享数据 2 个键、只能存 1 份;OOP 版三个实例存了 3 份(5/8/3)。 数据从【一块全局区】搬进【N 块堆对象】——于是【谁能改它】从所有人变成拿到对象的人。
30 秒实验
- 找一个你项目里的全局字典,想想它为什么没法同时服务两个用户。
- 把它改成类,看调用方需要改哪些行。
常见误区
- 以为有 class 就是 OOP。数据还在全局区,等于没搬。
- 以为全局数据只是不优雅。它根本没法存第二份。
自测三问(点开对答案)
1. 过程式和 OOP 的数据各放在哪? —— 过程式放模块级(静态区)的全局数据,只有一份;OOP 放堆上的实例里,要几份造几个。 2. 实测两边差在哪? —— 全局版 2 个键、1 份数据;OOP 版 3 个对象、3 份独立数据(total 5/8/3)。 3. 为什么说 OOP 的特征是内存用法? —— 因为数据从一块全局区搬进 N 块堆对象,【谁能改它】才从所有人变成拿到对象的人。- ☐ 下一节(5.7 每个类只加载一个类信息)
相关笔记
- 没有解决全局变量问题和可重用性差的问题 — 480 条边怎么断的
- 每次创建实例都会使用堆区 — 每次 new 都占一块
- 类的功能之一:汇总 — 汇总在内存上是什么
- 🧩 面向对象是怎样工作的 · 索引