🧩 第二章 似是而非:面向对象与现实世界
📄 一句话概括

抛开比喻,对象只有状态、行为、标识三样;凑齐这三样就是对象,不需要现实原型。

📝 本节和前面几节的关系

2.6 拆掉了比喻,这一节给正式定义——从这一节起,讨论都以代码为准。

一、三要素

状态——实例变量,它现在是什么样。 行为——方法,它能做什么。 标识——它在内存里的地址,它是【哪一个】。 三样凑齐,它就是一个对象。一条都不能少:只有状态没有行为,那是数据结构。

二、怎么验证

实测:vars(c) 给出状态 {'n': 2}。 实测:dir(c) 里去掉下划线开头的、且能调用的,剩下 ['add']——这就是行为。 实测:id(c) 给出标识,一个每次运行都不同的数。 注意 id 那一栏每次都不一样——它只是这个物的编号,不是内容。

三、没有现实原型的对象

Session(会话)——现实里指不出它是哪个物。 Transaction(事务)——同样指不出。 Mutex(互斥锁)——更抽象,它连【东西】都不算,是一种【约定】。 但它们在代码里都是标准的对象:有状态、有行为、有标识。

四、定义带来的自由

既然不要求现实原型,那就可以放心地为【软件自己的需要】造概念。 缓存、连接池、事件总线、仓储——这些都不是现实里的物,却是好软件的必需品。 反过来也成立:现实里有的东西,不一定要建成类。 判断标准只有一条:它在这个系统里有没有【状态+行为】需要维护。

五、动手 Demo:明确定义为编程结构

造一个最简单的计数器对象,把三要素逐一验出来。 状态看 vars(),行为看 dir() 里能调用的,标识看 id()。

class Counter:
    def __init__(self): self.n = 0
    def add(self):
        self.n += 1
        return self.n

c = Counter(); c.add(); c.add()
print("状态", vars(c))
print("行为", [m for m in dir(c)
      if not m.startswith("_") and callable(getattr(c, m))])
print("标识", id(c) % 1000000)

实跑输出:

--------------------------------------------------------------------
明确定义为编程结构
--------------------------------------------------------------------

  抛开比喻,对象在编程里只有三样东西:**状态、行为、标识。**

      class Counter:  n / add()
      c = Counter(); c.add(); c.add()

  要素     在代码里是什么     实测值
  ---------------------------------------------
  状态     实例变量           {'n': 2}
  行为     方法               ['add']
  标识     内存地址           id 末六位 482432

  **这三样凑齐,它就是一个对象——不需要它在现实里对应什么东西。**
  标识那一栏每次运行都不一样,**它只是【这个物】的编号,不是内容。**
  这句话很重要:**软件里的对象,可以完全没有现实原型。**
  比如 `Session`(会话)、`Transaction`(事务)、`Mutex`(互斥锁)——
  **现实世界里你指不出它们是哪个【物】。**

实测:状态 {'n': 2}、行为 ['add']、标识是一个六位数。 这三样凑齐就是对象——不需要它在现实里对应任何东西。

30 秒实验

  1. 给你的一个类做一次三要素检查,看缺不缺。
  2. 想一想:只有状态没有行为的类,算不算对象。

常见误区

  1. 以为对象必须有现实原型。Session、Mutex 都没有。
  2. 以为 id() 是内容摘要。它只是身份编号,每次运行都变。
自测三问(点开对答案) 1. 对象的三要素是什么? —— 状态(实例变量)、行为(方法)、标识(内存地址)。 2. 怎么在代码里验这三样? —— 状态看 vars(),行为看 dir() 里能调用的成员,标识看 id()。 3. 没有现实原型的对象存在吗? —— 存在而且是必需的:Session、Transaction、Mutex、缓存、连接池都是。

相关笔记