一文搞懂Python的abc.ABC基类

作者:

文章目录
  • 先说结论:不用也“能跑”,但容易乱。
  • 很多人以为“抽象方法就不能写代码”,其实这是误解。 在 Python 中,抽象方法完全可以有实现: from abc import ABC, abstractmethod class Base(ABC): @abstractmethod def process(self, data): print(“Base processing:”, data) 子类必须重写 process,但可以通过 super() 调用父类的实现: class Child(Base): def process(self, data): super().process(data) print(“Child extra processing:”, data) c = Child() c.process(“hello”) # 输出: # Base processing: hello # Child extra processing: hello 所以: @abstractmethod 的意义不是“这里不能写代码”,而是“子类必须重写这个方法”。
  • abc 还提供了更多装饰器,可以和 @abstractmethod 组合使用。
  • 在内部,ABC 是用一个特殊的 metaclass —— ABCMeta 实现的。 等价写法其实是: from abc import ABCMeta class MyABC(metaclass=ABCMeta): … 但日常开发中,不推荐直接用 ABCMeta,而是: from abc import ABC class MyABC(ABC): … 因为: 继承 ABC 可读性更好; 很多工具 / IDE 都默认识别这种模式。 简单记住: 想定义抽象基类,只需要继承 ABC,无需直接操作 ABCMeta。
  • Python 一直强调“鸭子类型”: 像鸭子一样走路、像鸭子一样叫,那它就是鸭子。 按照鸭子类型哲学,你甚至不用继承任何基类,方法名对得上就可以被当成“鸭子”。 那抽象基类是不是和鸭子类型相矛盾?其实: 抽象基类强调 显式的接口约束; 鸭子类型强调 行为匹配即可。 在大型项目、多人协作中,显式约束通常更安全、更易维护;在小脚本、试验性代码中,随意一点无可厚非。 所以: abc.ABC 不是为了推翻鸭子类型,而是在需要“约束和清晰接口”的地方,给你一把更强的工具。
  • 当你写的类“本来就是用来被继承”的时候,用 ABC 明确表达意图; 所有需要子类强制实现的方法,都应该加上 @abstractmethod; 抽象方法可以带默认实现,但请在 docstring 中说明子类是否需要调用 super(); 不要滥用: 小型脚本、一次性代码,没必要刻意引入 ABC; 对外暴露的库、框架、长期维护的项目,强烈建议使用。
  • abc.ABC 是 Python 用来定义 抽象基类 的基类; 你通过继承 ABC + @abstractmethod 来定义“必须由子类实现”的接口; 它的作用是:让接口约定更明确,让错误更早暴露,让多人协作更安全。 如果你在写库、写框架、写可复用组件,abc.ABC 是值得熟练掌握的工具。 到此这篇关于一文搞懂 Python 的 abc.ABC基类的文章就介绍到这了,更多相关Python abc.ABC 内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Python基类函数的重载与调用实例分析 python抽象基类用法实例分析 python 接口_从协议到抽象基类详解 python异常基类BaseException详解 Python抽象基类的定义与使用方法 浅析Python中接口与抽象基类的使用 python Exception常规异常基类详解 一文详解python中抽象基类使用指南 从基础到高级详解Python中接口与抽象基类的使用指南
  • 目录
    • 一、abc.ABC是什么?
      • 1. 它来自哪个模块?
      • 2. 抽象基类(Abstract Base Class)是什么?
    • 二、为什么要用abc.ABC?不用行不行?
      • 1. 不用abc.ABC会出现的问题
      • 2. 使用abc.ABC带来的好处
    • 三、abc.ABC的基本用法
      • 1. 定义抽象基类
      • 2. 定义子类并实现抽象方法
    • 四、抽象方法可以有方法体吗?
      • 五、抽象属性、类方法、静态方法
        • 1. 抽象属性
        • 2. 抽象类方法 / 静态方法
      • 六、ABC与ABCMeta的关系
        • 七、abc.ABC在实际工程中的使用场景
          • 场景 1:定义统一接口,屏蔽实现细节
          • 场景 2:插件系统或扩展点
          • 场景 3:为类型检查 / IDE 提供更好支持
        • 八、和“鸭子类型”的关系
          • 九、使用abc.ABC的最佳实践
            • 十、总结:一句话记住abc.ABC

              在 Python 里,abc.ABC 是“抽象基类(Abstract Base Class)”的基类。你可以把它理解为:

              一个“专门用来被继承的类”,它本身不一定能直接用来实例化,主要作用是 规定接口、做类型约束

              在日常写 Python 类时,你可能见过这样的代码:

              from abc import ABC, abstractmethod
              
              class Animal(ABC):
                  @abstractmethod
                  def speak(self):
                      pass
              

              这里的 ABC 是什么?为什么要这么写?不写会怎样?这篇文章就来系统讲清楚:abc.ABC 到底是干嘛用的、怎么用、什么时候值得用。

              ABC 定义在标准库 abc 模块中:

              from abc import ABC
              

              abc 全称 Abstract Base Classes,即“抽象基类”。
              ABC 就是一个“抽象基类的基类”。

              抽象基类(简称 ABC)是用来 定义接口规范 的类,它通常具有这些特点:

              1. 不打算直接被实例化(至少设计上是这样);
              2. 定义了一些必须由子类实现的方法
              3. 在 Python 中,这些“必须实现的方法”是通过 @abstractmethod(以及其他类似的装饰器)标记的。

              通俗理解:

              抽象基类是“接口模板”,子类是“真正干活的实现”。

              先说结论:不用也“能跑”,但容易乱

              想象你写了一个“动物”类:

              class Animal:
                  def speak(self):
                      raise NotImplementedError
              

              约定所有子类都要重写 speak
              问题在于:

              • 可以实例化 Animal,直到你调用 speak() 才会在运行时爆出 NotImplementedError
              • 没有任何机制在 类定义阶段 检查“子类到底有没有实现 speak”。

              这意味着:

              • 错误暴露得晚(运行时才知道);
              • 新人接手代码时,不知道哪些方法是强制要实现的,只能靠文档或注释。

              有了 abc.ABC@abstractmethod

              from abc import ABC, abstractmethod
              
              class Animal(ABC):
                  @abstractmethod
                  def speak(self):
                      pass
              

              好处立刻显现:

              1. 不能直接实例化抽象类

                a = Animal()  # TypeError: Can't instantiate abstract class Animal with abstract method speak
                
              2. 子类必须实现所有抽象方法才能被实例化

                class Dog(Animal):
                    pass
                
                Dog()  # TypeError: Can't instantiate abstract class Dog with abstract method speak
                

                一旦子类漏实现了 speak,在你实例化它的时候就会立刻报错。

              3. 抽象方法在 IDE / 文档里会有明确标记,接口规范更加清晰。

              总结一下:

              不用可以,但用了更安全、更清晰、更工程化。

              from abc import ABC, abstractmethod
              
              class Animal(ABC):
                  @abstractmethod
                  def speak(self):
                      """让动物发出叫声"""
                      pass
              
                  def sleep(self):
                      """普通实例方法,可以有默认实现"""
                      print("Zzz...")
              

              这里重点:

              • Animal 继承了 ABC,因此它是一个抽象基类;
              • speak 上加了 @abstractmethod,代表是抽象方法;
              • sleep 是普通方法,可以给出一个默认实现。

              class Dog(Animal):
                  def speak(self):
                      print("Woof!")
              
              class Cat(Animal):
                  def speak(self):
                      print("Meow~")
              

              现在:

              Dog().speak()  # Woof!
              Cat().sleep()  # Zzz...
              

              而如果写:

              class Fish(Animal):
                  # 没有实现 speak
                  pass
              
              Fish()  # TypeError: Can't instantiate abstract class Fish with abstract method speak
              

              很多人以为“抽象方法就不能写代码”,其实这是误解。

              在 Python 中,抽象方法完全可以有实现:

              from abc import ABC, abstractmethod
              
              class Base(ABC):
                  @abstractmethod
                  def process(self, data):
                      print("Base processing:", data)
              

              子类必须重写 process,但可以通过 super() 调用父类的实现:

              class Child(Base):
                  def process(self, data):
                      super().process(data)
                      print("Child extra processing:", data)
              
              c = Child()
              c.process("hello")
              # 输出:
              # Base processing: hello
              # Child extra processing: hello
              

              所以:

              @abstractmethod 的意义不是“这里不能写代码”,而是“子类必须重写这个方法”。

              abc 还提供了更多装饰器,可以和 @abstractmethod 组合使用。

              from abc import ABC, abstractmethod
              
              class Shape(ABC):
                  @property
                  @abstractmethod
                  def area(self):
                      """返回面积"""
                      pass
              

              子类必须实现属性 area

              class Circle(Shape):
                  def __init__(self, r):
                      self.r = r
              
                  @property
                  def area(self):
                      return 3.14 * self.r * self.r
              

              如果子类不实现该属性,实例化时会报 TypeError

              from abc import ABC, abstractmethod
              
              class Serializer(ABC):
                  @classmethod
                  @abstractmethod
                  def from_string(cls, s: str):
                      """从字符串反序列化"""
                      pass
              
                  @staticmethod
                  @abstractmethod
                  def validate(data):
                      """验证数据是否合法"""
                      pass
              

              子类必须实现 from_stringvalidate

              class JsonSerializer(Serializer):
                  @classmethod
                  def from_string(cls, s: str):
                      import json
                      return json.loads(s)
              
                  @staticmethod
                  def validate(data):
                      return isinstance(data, (dict, list))
              

              在内部,ABC 是用一个特殊的 metaclass —— ABCMeta 实现的。

              等价写法其实是:

              from abc import ABCMeta
              
              class MyABC(metaclass=ABCMeta):
                  ...
              

              但日常开发中,不推荐直接用 ABCMeta,而是:

              from abc import ABC
              
              class MyABC(ABC):
                  ...
              

              因为:

              • 继承 ABC 可读性更好;
              • 很多工具 / IDE 都默认识别这种模式。

              简单记住:

              想定义抽象基类,只需要继承 ABC,无需直接操作 ABCMeta。

              例如设计一个消息发送系统,希望支持多种渠道(邮箱、短信、钉钉等):

              from abc import ABC, abstractmethod
              
              class Notifier(ABC):
                  @abstractmethod
                  def send(self, to, message):
                      """发送消息"""
                      pass
              

              各种具体实现:

              class EmailNotifier(Notifier):
                  def send(self, to, message):
                      print(f"Email to {to}: {message}")
              
              class SMSNotifier(Notifier):
                  def send(self, to, message):
                      print(f"SMS to {to}: {message}")
              

              业务代码只依赖 Notifier 接口:

              def notify_user(notifier: Notifier, user, text):
                  notifier.send(user.contact, text)
              

              其中 notifier 可以是任意符合接口的实现类。
              日后需要新增一个 DingTalkNotifier,只要继承 Notifier 并实现 send 即可。

              例如你写了一个框架,想让别人“插入自己的算法”,就可以用 abc.ABC 规定一个基类,让所有插件作者都按这个接口实现:

              class Plugin(ABC):
                  @abstractmethod
                  def name(self) -> str:
                      pass
              
                  @abstractmethod
                  def run(self, data):
                      pass
              

              框架侧逻辑:

              def run_all_plugins(plugins, data):
                  for p in plugins:
                      print(f"Running plugin: {p.name()}")
                      p.run(data)
              

              插件作者只需要按照约定继承并实现方法。
              如果漏实现,就会在实例化 / 注册时直接报错,避免线上才发现错误。

              抽象基类为 静态类型检查 提供了很好的基础:

              def do_something(animal: Animal):
                  animal.speak()
              

              类型检查工具(如 mypy、pyright)能够根据 Animal 的定义更准确地判断类型是否匹配;IDE 也能更好地做自动补全。

              Python 一直强调“鸭子类型”:

              像鸭子一样走路、像鸭子一样叫,那它就是鸭子。

              按照鸭子类型哲学,你甚至不用继承任何基类,方法名对得上就可以被当成“鸭子”。

              那抽象基类是不是和鸭子类型相矛盾?其实:

              • 抽象基类强调 显式的接口约束
              • 鸭子类型强调 行为匹配即可

              在大型项目、多人协作中,显式约束通常更安全、更易维护
              在小脚本、试验性代码中,随意一点无可厚非。

              所以:

              abc.ABC 不是为了推翻鸭子类型,而是在需要“约束和清晰接口”的地方,给你一把更强的工具。

              1. 当你写的类“本来就是用来被继承”的时候,用 ABC 明确表达意图;
              2. 所有需要子类强制实现的方法,都应该加上 @abstractmethod
              3. 抽象方法可以带默认实现,但请在 docstring 中说明子类是否需要调用 super()
              4. 不要滥用:
                • 小型脚本、一次性代码,没必要刻意引入 ABC
                • 对外暴露的库、框架、长期维护的项目,强烈建议使用。

              • abc.ABC 是 Python 用来定义 抽象基类 的基类;
              • 你通过继承 ABC + @abstractmethod 来定义“必须由子类实现”的接口;
              • 它的作用是:让接口约定更明确,让错误更早暴露,让多人协作更安全。

              如果你在写库、写框架、写可复用组件,abc.ABC 是值得熟练掌握的工具。

              到此这篇关于一文搞懂 Python 的 abc.ABC基类的文章就介绍到这了,更多相关Python abc.ABC 内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

              您可能感兴趣的文章:

              • Python基类函数的重载与调用实例分析
              • python抽象基类用法实例分析
              • python 接口_从协议到抽象基类详解
              • python异常基类BaseException详解
              • Python抽象基类的定义与使用方法
              • 浅析Python中接口与抽象基类的使用
              • python Exception常规异常基类详解
              • 一文详解python中抽象基类使用指南
              • 从基础到高级详解Python中接口与抽象基类的使用指南

              站内搜索