← 返回题目列表

Python 的抽象基类(ABC)是什么?abstractmethod 怎么用?

高频 中等 第 5 / 21 题 更新于 2026/07/31
PythonABC抽象基类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 提供了一堆现成的抽象基类(IterableSequenceMapping 等),可以用 isinstance(x, Iterable) 判断「是否可迭代」;还能用 register() 把一个类「注册」为虚子类(不用真继承也能通过 isinstance 检查)。核心记忆:ABC 是不能实例化、用 @abstractmethod 强制子类实现接口的基类;子类没实现全部抽象方法就无法实例化;collections.abc 有现成的(Iterable/Sequence)。

详细版

ABC 的用法

元素作用
class C(ABC)定义抽象基类(继承 abc.ABC)
@abstractmethod标记抽象方法(子类必须实现)
实例化 ABCTypeError(不能直接实例化)
子类未实现抽象方法实例化时抛 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——它定义了 IterableIteratorSequenceMappingCallable 等标准抽象基类,一方面可以 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__indexcount;继承 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 注册虚子类不检查;是结构化鸭子类型」。