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

类是一份模板,实例是按模板做出来的物;状态相同不等于同一个物,而类自己也是一个对象。

📝 本节和前面几节的关系

2.1 把名词变成了类,这一节把【类】和【实例】分清——这是后面所有讨论的地基。

一、模板与物

类是模板:它规定有哪些字段、哪些方法。 实例是按模板做出来的一个一个具体的物:每个都有自己的字段值。 所以 Customer 是类,Customer("小林") 是实例。一份模板,可以做出无数个物。

二、字段一样,也不是同一个

造两个同名同电话的顾客:状态完全一样(a.name == b.name 是 True)。 但它们不是同一个物:a is b 是 False,a == b 也是 False。 为什么 == 也是 False?因为没写 __eq__ 时,== 默认比的也是身份。 这是很多人踩过的坑:以为内容一样就相等,其实默认比的是【是不是它】。

三、类本身也是对象

实测 type(Customer) 不是 Customer,而是 type。 也就是说,类本身是 type 的一个实例——所以类也能被传来传去、也能运行时动态创建。 这一条在很多语言里成立(Java 的 Class 对象、Python 的元类),只是不太常用。

四、这一对区分后面还会用到

5.8 讲【每次创建实例都会使用堆区】——说的就是实例。 5.7 讲【每个类只加载一个类信息】——说的就是类。 5.10 讲【复制存储实例的变量要小心】——根子就在【状态相同 ≠ 同一个物】。 所以这一节看着简单,其实是给第 5 章铺路。

五、动手 Demo:类指类型,实例指具体的物

造两个状态完全一样的实例,逐条对比身份与相等。 再看一眼类本身是什么类型的对象。

class Customer:
    def __init__(self, name):
        self.name = name

a = Customer("小林")
b = Customer("小林")
checks = [
    ("a.name == b.name", a.name == b.name),
    ("a is b", a is b),
    ("a == b", a == b),
    ("id(a) == id(b)", id(a) == id(b)),
    ("type(Customer).__name__", type(Customer).__name__),
    ("type(Customer) is type", type(Customer) is type),
]
for k, v in checks:
    print(k, v)

实跑输出:

--------------------------------------------------------------------
类指类型,实例指具体的物
--------------------------------------------------------------------

  【类】是一份模板,**【实例】是按模板做出来的一个一个具体的物。**
  两个坑全在细节里,真跑一遍就看得见。

      a = Customer("小林")
      b = Customer("小林")

  表达式                      实测结果     说明了什么
  ----------------------------------------------------------------
  a.name == b.name            True         状态一样
  a is b                      False        不是同一个物
  a == b                      False        没写 __eq__,== 比身份
  id(a) == id(b)              False        地址不同
  type(Customer).__name__     type         类也是对象
  type(Customer) is type      True         它是 type 的实例

  **看第 1 行和第 3 行**:状态一样(True),却不是同一个物(False)。
  而且 `==` 默认比的也是身份——**要按内容相等,得自己写 `__eq__`。**

  **记住这一对区分:状态相同 ≠ 是同一个物。**
  后面 5.10 讲【复制实例变量要小心】,根子就在这里。

实测:状态一样(True),a is b 和 a == b 都是 False。 == 默认比的也是身份——要按内容相等,得自己写 __eq__。

30 秒实验

  1. 给自己写的类加上 __eq__,看 a == b 会不会变 True。
  2. 打印 type(你的类),确认它也是 type。

常见误区

  1. 以为字段值一样就是同一个对象。身份和状态是两回事。
  2. 以为 == 比的是内容。没写 __eq__ 时它比的是身份。
自测三问(点开对答案) 1. 类和实例分别是什么? —— 类是模板,规定有哪些字段和方法;实例是按模板做出来的具体的物,各有自己的字段值。 2. 两个字段完全一样的实例相等吗? —— 不一定。没写 __eq__ 时 == 比的是身份,所以两个独立实例既不 is 也不 ==。 3. 类本身是什么? —— 类也是对象——它是 type 的实例,所以能被传递、能在运行时动态创建。

相关笔记