📄 一句话概括
所有类的继承链末端都是同一个祖先——一个空类一行代码没写,白拿了 29 个名字。
📝 本节和前面几节的关系
6.3 说标准库可靠,这一节说为什么不同来源的类能凑在一起用。
一、一个共同的祖先
实测 Leaf → Mid → Plain → object——再往上没有了。
所有类都自动继承 Object,不用你写。
这就是为什么【所有对象都像对象】:都能打印、都能比较、都能放进字典当键。
二、白拿了什么
实测空类 dir() 出来 29 个名字,全部继承自 object。
其中特殊方法 24 个:__str__、__eq__、__hash__、__repr__…
实测三件事都能跑:str(x) 有长度(类名+地址)、x == y 返回 False、hash(x) 有值。
一行代码没写,白拿 29 个名字。
三、这保证了互操作
类库之间不需要认识对方,只要都遵守 Object 的规矩。
json 能把任何对象塞进列表,collections 的容器能装任何对象——因为它们都【像对象】。
没有这个共同祖先,每个库都得为别人的类型写一遍适配。
四、该重写哪几个
__repr__:默认只给类名和地址,重写之后打印有内容(第五章那个 dump 的苦就靠它缓解)。
__eq__:默认比身份,两个内容相同的对象也判不等。
注意配套:重写 __eq__ 通常要一起重写 __hash__,否则对象不能当字典键。
五、动手 Demo:将 Object 类作为祖先类的继承结构
看一条三层继承链的末端是什么。 再数一数一个空类到底白拿了多少名字。
class Plain: pass
class Mid(Plain): pass
class Leaf(Mid): pass
print([c.__name__ for c in Leaf.__mro__])
print(len(dir(Plain))) # 白拿了多少
print(len(str(Plain()))) # 照样能打印
print(hash(Plain()) is not None) # 照样能哈希
实跑输出:
--------------------------------------------------------------------
将 Object 类作为祖先类的继承结构
--------------------------------------------------------------------
所有类的继承链末端都是同一个祖先——**Object**。实测:
Leaf 的继承链 Leaf → Mid → Plain → object
祖先类名字 object
Object 提供的公开方法 0 个
Object 提供的特殊方法 24 个(__str__/__eq__/__hash__ 等)
空类一共继承了多少名字 29 个
一个空类照样能用:
str(Plain()) 的长度 55 个字符(类名+地址)
Plain() == Plain() False
hash(Plain()) 存在吗 True
**一行代码没写,白拿了 29 个名字。**这就是祖先类的价值。
**它保证了【所有对象都像对象】**:都能打印、都能比较、都能放进字典当键。
**所以类库之间可以互操作**——它们不需要认识对方,只要都遵守 Object 的规矩。
实测:继承链是 Leaf → Mid → Plain → object,空类白拿了 29 个名字。 所有对象都像对象——类库之间不需要认识对方,只要都遵守 Object 的规矩就能互操作。
30 秒实验
- 打印
类型.__mro__,确认你写的类末端也是 object。 - 给你的类重写
__repr__,对比重写前后打印的差别。
常见误区
- 以为 object 是个可选的父类。它是所有类的默认祖先。
- 以为重写 eq 就够了。通常要连 hash 一起改。
自测三问(点开对答案)
1. 所有类的祖先是谁?实测继承链是什么? —— object;实测 Leaf → Mid → Plain → object,末端就是它。 2. 一个空类白拿了多少名字? —— 29 个(其中特殊方法 24 个),所以空类也能打印、比较、哈希。 3. 这个共同祖先保证了什么? —— 互操作:不同来源的类不需要认识对方,只要都像对象,就能塞进同一个容器、被同一个库处理。- ☐ 下一节(6.5 框架存在各种含义)
相关笔记
- 孤立的实例由垃圾回收处理 — dump 时的苦
- 框架存在各种含义 — 下一类复用单位
- ☕ Java基础 · 索引 — Object 类与 equals/hashCode
- 🧩 面向对象是怎样工作的 · 索引