Python 的抽象基类(ABC)是什么?abstractmethod 怎么用?
简化版
**抽象基类(Abstract Base Class,ABC)是「不能被直接实例化、只定义接口(规定子类必须实现哪些方法)的基类」——它用来强制子类遵守一个「契约」:谁继承我,就必须实现我标了 @abstractmethod 的方法,否则实例化时报错。**用法:继承 abc.ABC(或用 metaclass=ABCMeta),把「子类必须实现」的方法加 @abstractmethod 装饰器。例子:class Shape(ABC): @abstractmethod def area(self): ...——Shape() 直接实例化会报 TypeError,子类 Circle(Shape) 必须实现 area 才能实例化。核心价值:① 强制接口——把「子类必须实现某方法」从「文档约定」变成「运行时强制」(不实现就实例化失败,提前暴露错误);② 表达设计意图——明确「这是一个抽象概念,只能被继承、不能直接用」。相关:collections.abc 提供了一堆现成的抽象基类(Iterable、Sequence、Mapping 等),可以用 isinstance(x, Iterable) 判断「是否可迭代」;还能用 register() 把一个类「注册」为虚子类(不用真继承也能通过 isinstance 检查)。核心记忆:ABC 是不能实例化、用 @abstractmethod 强制子类实现接口的基类;子类没实现全部抽象方法就无法实例化;collections.abc 有现成的(Iterable/Sequence)。
详细版
ABC 的用法:
| 元素 | 作用 |
|---|---|
class C(ABC) | 定义抽象基类(继承 abc.ABC) |
@abstractmethod | 标记抽象方法(子类必须实现) |
| 实例化 ABC | 抛 TypeError(不能直接实例化) |
| 子类未实现抽象方法 | 实例化时抛 TypeError |
C.register(D) | 把 D 注册为虚子类(不用真继承) |
collections.abc | 现成的抽象基类(Iterable 等) |
from abc import ABC, abstractmethod
# 定义抽象基类
class Shape(ABC):
@abstractmethod
def area(self):
"""子类必须实现"""
...
@abstractmethod
def perimeter(self):
...
def describe(self): # 普通方法(子类继承,非抽象)
return f"面积 {self.area()}, 周长 {self.perimeter()}"
# 不能实例化抽象基类
# Shape() # ✗ TypeError: Can't instantiate abstract class Shape
# 子类必须实现所有抽象方法
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return 3.14 * self.r ** 2
def perimeter(self): return 2 * 3.14 * self.r
c = Circle(5) # ✓(实现了 area 和 perimeter)
print(c.describe()) # 用继承的普通方法
# 子类没实现全部抽象方法 → 也不能实例化
class Square(Shape):
def area(self): return 1 # 只实现了 area,没实现 perimeter
# Square() # ✗ TypeError(perimeter 未实现)
# abstractmethod 配合 property/classmethod
class Base(ABC):
@property
@abstractmethod
def name(self): ... # 抽象属性
# collections.abc 现成的抽象基类
from collections.abc import Iterable, Sequence
print(isinstance([1,2], Iterable)) # True(list 是可迭代的)
print(isinstance([1,2], Sequence)) # True
# register:注册虚子类(不用真继承)
class MyABC(ABC):
@abstractmethod
def do(self): ...
class Plain: # 没继承 MyABC
def do(self): return 1
MyABC.register(Plain) # 注册为虚子类
print(isinstance(Plain(), MyABC)) # True(虽然没继承)
⚠️ 核心价值:抽象基类把「子类应该实现哪些方法」从『文档里的口头约定』变成『运行时强制的契约』——标了
@abstractmethod的方法,子类不实现就无法实例化(实例化时立刻TypeError),错误在「创建对象」这一刻就暴露,而不是等到「调用那个没实现的方法」时才崩溃。这解决了「鸭子类型太松」的问题:普通继承里,子类忘了实现某方法,Python 不会拦你、直到真的调用到才AttributeError(可能很晚、在生产环境);ABC 让「必须实现的接口」有了强制力。两个层面的用途:① 自己定义抽象基类——当你设计一个「框架/基类」、要求所有子类都实现某组方法(如插件系统、策略接口),用 ABC + abstractmethod 强制;② 用现成的collections.abc——它定义了Iterable、Iterator、Sequence、Mapping、Callable等标准抽象基类,一方面可以isinstance(x, Iterable)做「基于能力的类型判断」(比isinstance(x, list)更通用——判断「能不能迭代」而非「是不是列表」),另一方面继承它们能自动获得一些混入方法(如继承Sequence实现__getitem__/__len__就白得__contains__/__iter__/index/count)。register()是「不用真继承也能算子类」的机制,用于把无法修改的第三方类纳入 isinstance 体系。
完整版教学
一、抽象基类是什么
先理解 ABC 的概念:
抽象基类(Abstract Base Class, ABC):
一个"只定义接口、不能直接实例化"的基类
规定"子类必须实现哪些方法"(契约)
两个核心特征:
① 不能直接实例化(它是"抽象概念")
Shape() → TypeError
② 有抽象方法(@abstractmethod 标记)
子类必须实现,否则子类也不能实例化
定义方式:
from abc import ABC, abstractmethod
class MyABC(ABC): # 继承 ABC
@abstractmethod
def must_implement(self): ...
# 或用 metaclass:
class MyABC(metaclass=ABCMeta): ...
为什么叫"抽象":
抽象方法只有"签名"、没有(或不该有)实现
→ 它定义"要做什么",不定义"怎么做"(留给子类)
抽象类是"不完整的",不能直接用
类比其他语言:
Java 的 abstract class / interface
→ ABC 是 Python 版的"接口/抽象类"
普通基类 vs 抽象基类:
普通基类:可实例化、子类可选择性覆盖方法
抽象基类:不可实例化、子类必须实现抽象方法
所以 ABC=不能实例化+用@abstractmethod 规定子类必须实现的方法(接口契约)
抽象基类(Abstract Base Class, ABC):一个「只定义接口、不能直接实例化」的基类、规定「子类必须实现哪些方法」(契约)。两个核心特征:① 不能直接实例化(抽象概念,Shape() → TypeError)、② 有抽象方法(@abstractmethod 标记、子类必须实现否则也不能实例化)。定义方式:class MyABC(ABC): @abstractmethod def must_implement(self): ...(或用 metaclass=ABCMeta)。为什么叫「抽象」:抽象方法只有签名、没有实现(定义「要做什么」不定义「怎么做」、留给子类)、抽象类不完整不能直接用。类比其他语言:Java 的 abstract class/interface(ABC 是 Python 版的接口/抽象类)。普通基类 vs 抽象基类:普通基类可实例化、子类可选择性覆盖;抽象基类不可实例化、子类必须实现抽象方法。理解「ABC=不能实例化+@abstractmethod 规定子类必须实现的方法(接口契约);继承 abc.ABC 或 metaclass=ABCMeta;抽象方法只有签名(定义要做什么、不定义怎么做);类似 Java 接口/抽象类」,就理解了抽象基类。
二、abstractmethod 强制实现
理解 @abstractmethod 的强制机制:
@abstractmethod:标记"子类必须实现"的方法
强制机制(两道关卡):
① 抽象基类本身不能实例化:
class Shape(ABC):
@abstractmethod
def area(self): ...
Shape() # TypeError: Can't instantiate abstract class
② 子类没实现全部抽象方法 → 子类也不能实例化:
class Square(Shape):
def area(self): ... # 只实现了 area
# 若 Shape 还有别的抽象方法没实现 → Square() 也 TypeError
③ 子类实现了全部抽象方法 → 可以实例化:
class Circle(Shape):
def area(self): ... # 实现所有抽象方法
Circle() # ✓
检查时机:实例化时(不是定义时)
→ 定义 Square 类不报错(能定义)
→ Square() 实例化时才检查抽象方法是否都实现了
抽象方法可以有默认实现(子类用 super 调用):
class Base(ABC):
@abstractmethod
def process(self):
return "默认逻辑" # 有实现,但仍强制子类"声明"
class Sub(Base):
def process(self):
return super().process() + "扩展" # 可复用父类逻辑
→ 抽象方法有实现 = "必须覆盖,但可复用"
对比不用 ABC(鸭子类型):
普通基类:子类忘实现方法 → 调用时才 AttributeError(晚)
ABC:子类忘实现 → 实例化时 TypeError(早,创建就暴露)
所以@abstractmethod 两道关卡:ABC 不能实例化+子类没实现全部抽象方法也不能;实例化时检查
@abstractmethod:标记「子类必须实现」的方法。强制机制(两道关卡):① 抽象基类本身不能实例化(Shape() → TypeError)、② 子类没实现全部抽象方法 → 子类也不能实例化(只实现部分则 Square() TypeError)、③ 子类实现了全部抽象方法 → 可以实例化。检查时机:实例化时(不是定义时)——定义 Square 类不报错、Square() 实例化时才检查。抽象方法可以有默认实现(子类用 super 调用):@abstractmethod def process(self): return "默认逻辑"(有实现但仍强制子类声明、子类可 super().process() 复用)。对比不用 ABC(鸭子类型):普通基类子类忘实现方法调用时才 AttributeError(晚)、ABC 实例化时 TypeError(早、创建就暴露)。理解「@abstractmethod 两道关卡:ABC 不能实例化+子类没实现全部抽象方法也不能;实例化时检查(不是定义时);抽象方法可有默认实现(子类 super 复用);比鸭子类型早暴露错误」,就掌握了强制机制。
三、抽象属性、类方法、静态方法
理解 abstractmethod 和其他装饰器组合:
@abstractmethod 可以和 property/classmethod/staticmethod 组合:
组合时 @abstractmethod 放在"最内层"(最靠近函数)
抽象属性(property + abstractmethod):
class Base(ABC):
@property
@abstractmethod
def name(self): ... # 子类必须提供 name 属性
class Sub(Base):
@property
def name(self): return "sub"
→ 强制子类实现某个属性
抽象类方法:
class Base(ABC):
@classmethod
@abstractmethod
def create(cls): ...
→ 强制子类实现某个类方法(如工厂方法)
抽象静态方法:
class Base(ABC):
@staticmethod
@abstractmethod
def validate(x): ...
抽象属性的 setter:
class Base(ABC):
@property
@abstractmethod
def value(self): ...
@value.setter
@abstractmethod
def value(self, v): ...
装饰器顺序(重要):
@property / @classmethod / @staticmethod 在外
@abstractmethod 在内(紧贴函数)
→ 顺序反了可能失效
用途:
不只强制"方法",还能强制"属性、类方法、静态方法"
→ 完整地定义子类必须提供的接口
所以@abstractmethod 可组合 property/classmethod/staticmethod(放最内层),强制各种成员
@abstractmethod 可以和 property/classmethod/staticmethod 组合(组合时 @abstractmethod 放在「最内层」最靠近函数)。抽象属性(property + abstractmethod):@property @abstractmethod def name(self): ...(子类必须提供 name 属性)。抽象类方法:@classmethod @abstractmethod def create(cls): ...(强制子类实现工厂方法)。抽象静态方法:@staticmethod @abstractmethod def validate(x): ...。抽象属性的 setter:@value.setter @abstractmethod。装饰器顺序(重要):@property/@classmethod/@staticmethod 在外、@abstractmethod 在内(紧贴函数、顺序反了可能失效)。用途:不只强制方法、还能强制属性/类方法/静态方法(完整定义子类必须提供的接口)。理解「@abstractmethod 可组合 property/classmethod/staticmethod(@abstractmethod 放最内层紧贴函数);抽象属性/抽象类方法/抽象静态方法;顺序反了失效;完整定义子类接口」,就掌握了组合用法。
四、collections.abc 现成的抽象基类
理解标准库的抽象基类:
collections.abc:标准库提供的一堆抽象基类
定义了 Python 数据模型的"接口"
常见的:
Iterable 要实现 __iter__(可迭代)
Iterator __iter__ + __next__
Sequence __getitem__ + __len__(有序、可索引)
MutableSequence 可变序列(list 是)
Mapping __getitem__ + __len__ + __iter__(字典类)
MutableMapping 可变映射(dict 是)
Set / MutableSet
Callable __call__(可调用)
Hashable __hash__
两大用途:
① isinstance 做"基于能力"的类型判断:
from collections.abc import Iterable
isinstance([1,2], Iterable) # True(能迭代)
isinstance("abc", Iterable) # True
isinstance(42, Iterable) # False
→ 判断"能不能迭代",比 isinstance(x, list) 更通用
→ 判断"能力"(能迭代/能调用)而非"具体类型"
② 继承它们获得"免费的混入方法"(mixin):
from collections.abc import Sequence
class MySeq(Sequence):
def __getitem__(self, i): ... # 只实现这俩
def __len__(self): ...
# 自动获得:__contains__、__iter__、__reversed__、
# index、count(基于 __getitem__/__len__)
→ 实现最少的抽象方法,白得一堆派生方法
判断自定义类是否符合接口:
isinstance(my_obj, Sequence) # 检查是否符合序列协议
所以 collections.abc 有现成抽象基类(Iterable/Sequence/Mapping);isinstance 判断能力+继承得混入方法
collections.abc:标准库提供的一堆抽象基类(定义了 Python 数据模型的接口)。常见的:Iterable(__iter__)、Iterator(__iter__+__next__)、Sequence(__getitem__+__len__、有序可索引)、Mapping(字典类)、Callable(__call__)、Hashable(__hash__)等。两大用途:① isinstance 做「基于能力」的类型判断(isinstance([1,2], Iterable) 为 True、判断「能不能迭代」比 isinstance(x, list) 更通用、判断能力而非具体类型);② 继承它们获得「免费的混入方法」(mixin)(继承 Sequence 只实现 __getitem__/__len__、自动获得 __contains__/__iter__/index/count——实现最少抽象方法白得一堆派生方法)。判断自定义类是否符合接口:isinstance(my_obj, Sequence)。理解「collections.abc 有现成抽象基类(Iterable/Iterator/Sequence/Mapping/Callable/Hashable);用途①isinstance 判断能力(能迭代/能调用比 isinstance(list)通用)②继承得免费混入方法(Sequence 实现__getitem__/len__白得__contains/index/count)」,就掌握了 collections.abc。
五、register 虚子类与鸭子类型
理解 register 和 ABC 与鸭子类型的关系:
register:把一个类注册为"虚子类"(virtual subclass)
不用真继承,也能通过 isinstance/issubclass 检查
class Drawable(ABC):
@abstractmethod
def draw(self): ...
class Circle: # 没继承 Drawable
def draw(self): ...
Drawable.register(Circle) # 注册为虚子类
isinstance(Circle(), Drawable) # True(虽然没继承)
issubclass(Circle, Drawable) # True
virtual subclass 的特点:
① 不真正继承(Circle.__mro__ 里没有 Drawable)
② 通过 isinstance/issubclass 检查
③ 不强制实现抽象方法(register 不检查!)
④ 不获得 ABC 的方法(没有继承关系)
→ 只是"声明"这个类符合接口(信任,不验证)
用途:
① 把无法修改的第三方类纳入 isinstance 体系
② 让已有的类"算作"某接口的实现
__subclasshook__(自动虚子类):
某些 ABC 定义了 __subclasshook__,自动判断
→ 如 Iterable 的检查是"有没有 __iter__"
任何有 __iter__ 的类,isinstance(x, Iterable) 自动为 True
→ 这就是为什么 list 没继承 Iterable 却 isinstance 为 True
ABC vs 鸭子类型:
鸭子类型:不看类型、只看"有没有那个方法"(运行时)
ABC:显式的接口契约(实例化时强制/isinstance 检查)
→ ABC 是"结构化的鸭子类型"(给鸭子类型加约束和检查)
所以 register 注册虚子类(不真继承、不检查实现、只声明符合接口);__subclasshook__自动判断
register:把一个类注册为「虚子类(virtual subclass)」——不用真继承也能通过 isinstance/issubclass 检查(Drawable.register(Circle) 后 isinstance(Circle(), Drawable) 为 True 虽然没继承)。virtual subclass 的特点:① 不真正继承(MRO 里没有)、② 通过 isinstance/issubclass 检查、③ 不强制实现抽象方法(register 不检查!)、④ 不获得 ABC 的方法(只是「声明」符合接口、信任不验证)。用途:把无法修改的第三方类纳入 isinstance 体系。__subclasshook__(自动虚子类):某些 ABC 定义了它自动判断(如 Iterable 检查「有没有 __iter__」、任何有 __iter__ 的类 isinstance(x, Iterable) 自动为 True——这就是为什么 list 没继承 Iterable 却 isinstance 为 True)。ABC vs 鸭子类型:鸭子类型不看类型只看有没有方法(运行时)、ABC 是显式的接口契约(实例化时强制/isinstance 检查)——ABC 是「结构化的鸭子类型」。理解「register 注册虚子类(不真继承、不检查实现、只声明符合接口);subclasshook__自动判断(Iterable 检查有无__iter、所以 list 自动 isinstance);ABC 是结构化的鸭子类型(给鸭子类型加约束)」,就掌握了 register 和鸭子类型的关系。
六、总结与实践
总结抽象基类:
核心:
ABC = 不能实例化 + 用 @abstractmethod 强制子类实现接口
子类没实现全部抽象方法 → 也不能实例化(实例化时 TypeError)
定义:
class MyABC(ABC):
@abstractmethod
def method(self): ...
强制机制:
① ABC 本身不能实例化
② 子类未实现全部抽象方法也不能实例化
③ 检查时机:实例化时(不是定义时)
组合:
@abstractmethod + property/classmethod/staticmethod
(@abstractmethod 放最内层)
collections.abc(现成的):
Iterable/Sequence/Mapping/Callable 等
用途:isinstance 判断能力 + 继承得免费混入方法
register:
注册虚子类(不真继承、不检查实现、只声明)
价值:
把"子类必须实现"从文档约定变成运行时强制
错误在实例化时暴露(早),而非调用时(晚)
vs 鸭子类型:
ABC 是"结构化的鸭子类型"(加接口契约和检查)
实践建议:
① 设计框架/插件接口 → 用 ABC + abstractmethod 强制
② 判断"能力"(可迭代等) → isinstance + collections.abc
③ 自定义容器 → 继承 collections.abc 得混入方法
④ 别过度用(简单场景鸭子类型就够)
所以 ABC 用@abstractmethod 强制子类实现接口(实例化时检查),collections.abc 有现成的
核心:ABC = 不能实例化 + 用 @abstractmethod 强制子类实现接口(子类没实现全部抽象方法也不能实例化、实例化时 TypeError)。定义:class MyABC(ABC): @abstractmethod def method(self): ...。强制机制:① ABC 本身不能实例化、② 子类未实现全部抽象方法也不能、③ 检查时机是实例化时。组合:@abstractmethod + property/classmethod/staticmethod(放最内层)。collections.abc:Iterable/Sequence/Mapping/Callable(isinstance 判断能力 + 继承得混入方法)。register:注册虚子类(不真继承、不检查、只声明)。价值:把「子类必须实现」从文档约定变成运行时强制、错误在实例化时早暴露。实践建议:设计框架/插件接口用 ABC、判断能力用 isinstance + collections.abc、自定义容器继承 collections.abc、别过度用。理解「ABC 用@abstractmethod 强制子类实现接口(实例化时检查早暴露);collections.abc 有现成(Iterable/Sequence,isinstance 判断能力+继承得混入方法);register 注册虚子类;是结构化鸭子类型;框架/插件用 ABC、判断能力用 collections.abc」,就掌握了总结与实践。
记忆钩子:「抽象基类(Abstract Base Class,ABC)=不能直接实例化、用@abstractmethod 规定子类必须实现哪些方法的基类;定义:继承 abc.ABC(或 metaclass=ABCMeta),把子类必须实现的方法加@abstractmethod;★强制机制两道关卡:①抽象基类本身不能实例化(Shape()→TypeError)②子类没实现『全部』抽象方法也不能实例化(实例化时才检查、不是定义时);★核心价值:把『子类必须实现某方法』从『文档约定』变成『运行时强制的契约』——不实现就实例化失败、错误在创建对象时就暴露(而非鸭子类型下等到调用那个方法才 AttributeError、可能很晚);@abstractmethod 可和 property/classmethod/staticmethod 组合(@abstractmethod 放最内层紧贴函数),强制抽象属性/类方法;★collections.abc 提供现成抽象基类(Iterable/Iterator/Sequence/Mapping/Callable/Hashable):用途①isinstance(x,Iterable)做『基于能力』的类型判断(判断能不能迭代、比 isinstance(x,list)通用)②继承它们获得『免费的混入方法』(继承 Sequence 只实现__getitem__/len__就白得__contains/index/count);register()把类注册为『虚子类』(不用真继承也能通过 isinstance、但不检查是否实现抽象方法、只是声明符合接口);ABC 是『结构化的鸭子类型』(给鸭子类型加接口契约和检查)」。
七、常见误区与追问
- 误区:抽象基类可以直接实例化。 不能——只要一个类(继承了 ABC 或用 ABCMeta)里有至少一个
@abstractmethod标记的方法,直接实例化它就会抛TypeError: Can't instantiate abstract class;这正是「抽象」的含义——它是不完整的接口定义、只能被继承、不能直接创建对象;同理,子类如果没有实现父类全部的抽象方法,子类也仍然是抽象的、也不能实例化。 - 误区:子类没实现抽象方法,定义类时就会报错。 不是——检查发生在「实例化时」而不是「定义类时」:
class Square(Shape): pass(没实现抽象方法)这行代码本身不报错、类能正常定义,只有当你Square()尝试创建实例时才抛 TypeError;这是因为 Python 允许「抽象的中间层子类」(一个子类可以只实现部分抽象方法、留一部分给它的子类实现,它自己仍是抽象的)。 - 误区:@abstractmethod 标记的方法必须是空的、不能有实现。 可以有实现——抽象方法可以写默认逻辑(
@abstractmethod def process(self): return "默认"),这时它仍然强制子类「必须覆盖」这个方法(否则子类不能实例化),但子类的实现里可以用super().process()复用父类的默认逻辑;这种「有实现的抽象方法」用于「子类必须显式声明要用这个方法、但可以复用基类提供的部分逻辑」的场景。 - 误区:用 register 注册虚子类后,Python 会检查这个类是否实现了抽象方法。 不会——
ABC.register(SomeClass)只是「声明」SomeClass 算作这个 ABC 的(虚)子类、让isinstance/issubclass返回 True,但它不真正建立继承关系(MRO 里没有 ABC)、也不检查 SomeClass 是否实现了抽象方法、更不会让 SomeClass 获得 ABC 的任何方法;register 是「信任而不验证」的机制,主要用于把你无法修改的第三方类纳入 isinstance 体系;如果被注册的类其实没实现接口方法、调用时照样会 AttributeError。 - 追问:抽象基类相比普通基类或鸭子类型有什么好处? 核心好处是「把接口契约从约定变成强制」:普通基类里,你可以在文档或注释里说「子类应该实现 X 方法」,但 Python 不强制,子类忘了实现也能实例化、直到运行时真的调用 X 才抛 AttributeError(错误暴露得很晚、可能在生产环境);鸭子类型完全不检查类型、更松;而抽象基类用
@abstractmethod让「必须实现」有了运行时强制力——子类没实现全部抽象方法就无法实例化,错误在「创建对象」这一刻就暴露(fail fast、更早发现),而且 ABC 清晰地表达了「这是一个只能被继承的接口/抽象概念」的设计意图;所以在设计框架、插件系统、需要多个实现遵守统一接口的场景,ABC 比纯鸭子类型更安全可控。 - 追问:collections.abc 里的抽象基类有什么用? 两大用途:① 做「基于能力的类型判断」——
collections.abc定义了 Iterable、Iterator、Sequence、Mapping、Callable、Hashable 等标准抽象基类,用isinstance(x, Iterable)可以判断「x 能不能迭代」、isinstance(x, Callable)判断「能不能调用」,这比isinstance(x, list)更通用(关注「能力/协议」而非「具体类型」,任何实现了__iter__的对象都算 Iterable);② 快速实现自定义容器——继承这些抽象基类,你只需实现最少的几个抽象方法,就能「免费」获得一堆基于它们的派生方法(混入方法):比如继承Sequence只实现__getitem__和__len__,就自动获得__contains__、__iter__、__reversed__、index、count;继承MutableMapping实现几个核心方法就得到完整的字典接口;这大大减少了实现自定义容器的样板代码。 - 追问:为什么 list 没有继承 collections.abc.Iterable,但 isinstance([1,2], Iterable) 却是 True? 因为
Iterable这类抽象基类定义了__subclasshook__特殊方法,实现了「基于结构的自动子类判断」——Iterable.__subclasshook__会检查一个类「有没有__iter__方法」,只要有就认为它是 Iterable 的(虚)子类,不需要真的继承;list 实现了__iter__,所以isinstance([1,2], Iterable)自动返回 True,issubclass(list, Iterable)也是 True;这体现了 ABC「结构化鸭子类型」的思想——不看「是不是显式继承」、而看「有没有对应的能力(方法)」;不是所有 ABC 都有__subclasshook__(只有那些「单一方法就能判定」的简单协议才有,如 Iterable/Hashable/Callable),复杂的接口(如完整的 Sequence)通常需要真继承或 register。
八、加强记忆
抽象基类(Abstract Base Class, ABC)= 不能直接实例化、用 @abstractmethod 规定子类必须实现哪些方法的基类。定义:继承 abc.ABC(或 metaclass=ABCMeta),把子类必须实现的方法加 @abstractmethod。强制机制两道关卡:① 抽象基类本身不能实例化(Shape() → TypeError)、② 子类没实现「全部」抽象方法也不能实例化(实例化时才检查、不是定义时)。核心价值:把「子类必须实现某方法」从「文档约定」变成「运行时强制的契约」——不实现就实例化失败、错误在创建对象时就暴露(而非鸭子类型下等到调用那个方法才 AttributeError、可能很晚)。@abstractmethod 可和 property/classmethod/staticmethod 组合(@abstractmethod 放最内层紧贴函数),强制抽象属性/类方法。collections.abc 提供现成抽象基类(Iterable/Iterator/Sequence/Mapping/Callable/Hashable):用途 ① isinstance(x, Iterable) 做「基于能力」的类型判断(判断能不能迭代、比 isinstance(x, list) 通用)、② 继承它们获得「免费的混入方法」(继承 Sequence 只实现 __getitem__/__len__ 就白得 __contains__/index/count)。register() 把类注册为「虚子类」(不用真继承也能通过 isinstance、但不检查是否实现抽象方法、只是声明符合接口)。ABC 是「结构化的鸭子类型」(给鸭子类型加接口契约和检查)。一句话「ABC 不能实例化+@abstractmethod 强制子类实现接口(子类没实现全部抽象方法也不能实例化、实例化时检查、早暴露);collections.abc 有现成的(Iterable/Sequence,isinstance 判断能力+继承得混入方法);register 注册虚子类不检查;是结构化鸭子类型」。