Python 里的 ABC 指的是 Abstract Base Class,抽象基类 。它的核心作用是:定义一类对象应该具备哪些接口或行为,但不一定提供完整实现。换句话说,ABC 像是一份"类的契约":子类必须实现指定的方法,否则不能被实例化。
下面我们从概念、基本用法、典型示例、进阶机制和实际应用场景几个角度来系统梳理。
1. ABC 是什么?
在 Python 中,ABC 通常由标准库中的 abc 模块提供。
python
from abc import ABC, abstractmethod
其中:
ABC:抽象基类的基类。abstractmethod:用于标记抽象方法。- 抽象类:包含至少一个抽象方法的类。
- 抽象方法:只声明接口,要求子类必须实现的方法。
简单理解
假设你要设计一个"动物"系统:
- 所有动物都应该会"叫"。
- 但是不同动物叫声不同。
- 所以基类
Animal不应该直接规定具体叫声。 - 它只要求子类必须实现
speak()方法。
这时就可以使用 ABC。
2. 最基础的 ABC 示例
下面是一个最经典的抽象基类例子。
python
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "汪汪"
class Cat(Animal):
def speak(self):
return "喵喵"
dog = Dog()
cat = Cat()
print(dog.speak())
print(cat.speak())
输出:
python
汪汪
喵喵
这里的 Animal 是抽象基类。
它定义了一个抽象方法:
python
@abstractmethod
def speak(self):
pass
这表示:任何继承 Animal 的子类,都必须实现 speak() 方法。
3. 如果子类没有实现抽象方法会怎样?
如果子类没有实现抽象方法,那么这个子类依然是抽象类,不能被实例化。
python
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
pass
dog = Dog()
运行后会报错:
python
TypeError: Can't instantiate abstract class Dog with abstract method speak
意思是:
Dog这个类继承了抽象类Animal,但是没有实现speak()方法,所以不能创建对象。
4. ABC 的主要作用
ABC 的价值不只是"防止实例化",更重要的是它能帮助你设计更清晰的程序结构。
4.1 定义统一接口
比如你要设计多个支付方式:
- 支付宝支付
- 微信支付
- 银行卡支付
它们都应该有一个统一的 pay() 方法。
python
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount):
pass
class Alipay(Payment):
def pay(self, amount):
print(f"使用支付宝支付 {amount} 元")
class WechatPay(Payment):
def pay(self, amount):
print(f"使用微信支付 {amount} 元")
class BankCardPay(Payment):
def pay(self, amount):
print(f"使用银行卡支付 {amount} 元")
def checkout(payment: Payment, amount):
payment.pay(amount)
checkout(Alipay(), 100)
checkout(WechatPay(), 200)
checkout(BankCardPay(), 300)
输出:
python
使用支付宝支付 100 元
使用微信支付 200 元
使用银行卡支付 300 元
这里的 Payment 就是一个抽象基类,它规定所有支付类都必须实现:
python
pay(amount)
这样你的业务代码就可以面向统一接口编程,而不需要关心具体是哪种支付方式。
5. 抽象类中可以有普通方法吗?
可以。
抽象基类不仅可以有抽象方法,也可以包含普通方法、属性、初始化逻辑等。
python
from abc import ABC, abstractmethod
class Shape(ABC):
def __init__(self, name):
self.name = name
def describe(self):
print(f"这是一个图形:{self.name}")
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
super().__init__("圆形")
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
circle = Circle(5)
circle.describe()
print(circle.area())
输出:
python
这是一个图形:圆形
78.53975
这里:
describe()是普通方法,子类可以直接继承使用。area()是抽象方法,子类必须实现。Shape不能直接实例化。
6. 抽象方法可以有默认实现吗?
可以。
很多人以为抽象方法里面只能写 pass,其实不是。抽象方法也可以有默认实现,子类仍然必须显式覆盖它,但可以通过 super() 调用父类逻辑。
python
from abc import ABC, abstractmethod
class BaseLogger(ABC):
@abstractmethod
def log(self, message):
print(f"[基础日志] {message}")
class FileLogger(BaseLogger):
def log(self, message):
super().log(message)
print(f"写入文件:{message}")
logger = FileLogger()
logger.log("系统启动")
输出:
python
[基础日志] 系统启动
写入文件:系统启动
注意:
即使 BaseLogger.log() 有具体实现,只要它被 @abstractmethod 标记,子类就必须重写它。
7. 抽象属性:@property 和 @abstractmethod
ABC 不仅可以约束方法,也可以约束属性。
python
from abc import ABC, abstractmethod
class Employee(ABC):
@property
@abstractmethod
def salary(self):
pass
class Developer(Employee):
def __init__(self, base_salary):
self._salary = base_salary
@property
def salary(self):
return self._salary
dev = Developer(20000)
print(dev.salary)
输出:
python
20000
这里要求所有 Employee 子类必须提供 salary 属性。
8. 抽象类方法和抽象静态方法
ABC 也可以和 @classmethod、@staticmethod 一起使用。
8.1 抽象类方法
python
from abc import ABC, abstractmethod
class Database(ABC):
@classmethod
@abstractmethod
def connect(cls, url):
pass
class MySQLDatabase(Database):
@classmethod
def connect(cls, url):
print(f"MySQL 连接到:{url}")
MySQLDatabase.connect("mysql://localhost:3306/test")
输出:
python
MySQL 连接到:mysql://localhost:3306/test
8.2 抽象静态方法
python
from abc import ABC, abstractmethod
class Validator(ABC):
@staticmethod
@abstractmethod
def validate(value):
pass
class EmailValidator(Validator):
@staticmethod
def validate(value):
return "@" in value
print(EmailValidator.validate("test@example.com"))
print(EmailValidator.validate("invalid-email"))
输出:
python
True
False
9. ABC 和 isinstance() / issubclass()
ABC 可以用于类型判断。
python
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount):
pass
class Alipay(Payment):
def pay(self, amount):
print(f"支付宝支付 {amount} 元")
print(isinstance(Alipay(), Payment))
print(issubclass(Alipay, Payment))
输出:
python
True
True
这说明:
Alipay()是Payment的实例。Alipay是Payment的子类。
10. 虚拟子类:register()
ABC 还有一个较特殊的功能:注册虚拟子类。
即使某个类没有显式继承抽象基类,也可以被注册为它的"虚拟子类"。
python
from abc import ABC, abstractmethod
class Plugin(ABC):
@abstractmethod
def run(self):
pass
class ExternalPlugin:
def run(self):
print("外部插件运行")
Plugin.register(ExternalPlugin)
plugin = ExternalPlugin()
print(isinstance(plugin, Plugin))
print(issubclass(ExternalPlugin, Plugin))
plugin.run()
输出:
python
True
True
外部插件运行
注意:
ExternalPlugin 并没有继承 Plugin:
python
class ExternalPlugin:
...
但经过:
python
Plugin.register(ExternalPlugin)
之后,它会被认为是 Plugin 的虚拟子类。
重要提醒
register() 不会强制检查方法是否真的存在。
例如:
python
class BadPlugin:
pass
Plugin.register(BadPlugin)
print(isinstance(BadPlugin(), Plugin))
这仍然可能输出:
python
True
但 BadPlugin 并没有实现 run() 方法。
所以 register() 更像是一种类型声明,而不是严格校验机制。
11. __subclasshook__:自定义子类判断逻辑
__subclasshook__ 可以让你自定义 issubclass() 的判断规则。
例如,只要一个类有 run() 方法,就认为它是 Runnable 的子类。
python
from abc import ABC, abstractmethod
class Runnable(ABC):
@abstractmethod
def run(self):
pass
@classmethod
def __subclasshook__(cls, subclass):
if cls is Runnable:
if any("run" in base.__dict__ for base in subclass.__mro__):
return True
return NotImplemented
class Task:
def run(self):
print("任务运行")
class Person:
def walk(self):
print("人走路")
print(issubclass(Task, Runnable))
print(issubclass(Person, Runnable))
输出:
python
True
False
这里 Task 没有继承 Runnable,但是它有 run() 方法,因此被认为是 Runnable 的子类。
12. Python 内置的 ABC:collections.abc
Python 标准库中已经提供了很多常用的抽象基类,位于 collections.abc 中。
常见的有:
| ABC | 含义 | 典型方法 |
|---|---|---|
Iterable |
可迭代对象 | __iter__() |
Iterator |
迭代器 | __iter__()、__next__() |
Sequence |
序列 | __getitem__()、__len__() |
MutableSequence |
可变序列 | append()、insert() 等 |
Mapping |
映射类型 | keys()、items()、__getitem__() |
MutableMapping |
可变映射 | __setitem__()、__delitem__() |
Set |
集合 | __contains__()、__iter__()、__len__() |
Callable |
可调用对象 | __call__() |
这些 ABC 主要用于判断对象是否符合某种协议。
示例:判断对象是否可迭代
python
from collections.abc import Iterable
print(isinstance([1, 2, 3], Iterable))
print(isinstance("hello", Iterable))
print(isinstance(123, Iterable))
输出:
python
True
True
False
列表和字符串都可以迭代,而整数不能。
13. 自定义一个类似列表的类
下面我们用 collections.abc.MutableSequence 实现一个自定义列表类型。
python
from collections.abc import MutableSequence
class MyList(MutableSequence):
def __init__(self):
self._items = []
def __len__(self):
return len(self._items)
def __getitem__(self, index):
return self._items[index]
def __setitem__(self, index, value):
self._items[index] = value
def __delitem__(self, index):
del self._items[index]
def insert(self, index, value):
self._items.insert(index, value)
my_list = MyList()
my_list.append(10)
my_list.append(20)
my_list.append(30)
print(len(my_list))
print(my_list[0])
print(list(my_list))
my_list[1] = 200
print(list(my_list))
del my_list[0]
print(list(my_list))
输出:
python
3
10
[10, 20, 30]
[10, 200, 30]
[200, 30]
这里继承 MutableSequence 后,只要实现几个核心方法:
python
__len__
__getitem__
__setitem__
__delitem__
insert
就可以自动获得很多列表行为,例如:
python
append()
remove()
reverse()
extend()
pop()
这就是 ABC 的一个强大之处:它既定义接口,也能提供部分通用实现。
14. ABC 与普通继承的区别
ABC 本质上也是继承,但它更强调"接口约束"。
| 对比项 | 普通基类 | 抽象基类 ABC |
|---|---|---|
| 是否能直接实例化 | 通常可以 | 有抽象方法时不可以 |
| 是否强制子类实现方法 | 不强制 | 强制 |
| 主要目的 | 代码复用 | 接口规范 + 代码复用 |
| 常用模块 | 不需要额外模块 | abc |
| 常见场景 | 父类提供通用逻辑 | 框架、插件、支付、存储、驱动等 |
普通继承更像是:
"子类复用父类的代码。"
ABC 更像是:
"子类必须遵守父类定义的规范。"
15. ABC 与鸭子类型的关系
Python 是一门强调 鸭子类型 的语言。
所谓鸭子类型,就是:
如果一个对象走起来像鸭子,叫起来像鸭子,那它就可以被当作鸭子。
也就是说,Python 通常不关心对象到底是什么类型,只关心它有没有需要的方法。
例如:
python
class Dog:
def speak(self):
print("汪汪")
class Robot:
def speak(self):
print("机器人发声")
def make_it_speak(obj):
obj.speak()
make_it_speak(Dog())
make_it_speak(Robot())
即使 Dog 和 Robot 没有共同父类,只要它们都有 speak() 方法,就可以正常工作。
那为什么还需要 ABC?
因为在较大的项目中,完全依赖鸭子类型可能会带来一些问题:
- 接口不统一。
- 子类漏实现方法时,错误可能到运行很久之后才暴露。
- 阅读代码时不容易知道某类必须实现哪些能力。
- 框架或插件系统需要明确规范。
ABC 就是在动态语言中提供一种更明确的接口约束方式。
16. ABC 与 typing.Protocol 的区别
现代 Python 中,除了 ABC,还有一个常用概念是 Protocol。
它们都可以表达"接口",但关注点不同。
| 对比项 | ABC | Protocol |
|---|---|---|
| 所在模块 | abc |
typing |
| 主要用途 | 运行时接口约束 | 静态类型检查 |
| 是否要求显式继承 | 通常需要 | 不一定 |
| 能否阻止实例化 | 可以 | 通常不用于这个目的 |
是否影响 isinstance() |
可以 | 默认不行,需 @runtime_checkable |
| 适合场景 | 框架基类、插件系统、运行时检查 | 类型标注、mypy/pyright 检查 |
ABC 示例
python
from abc import ABC, abstractmethod
class Sender(ABC):
@abstractmethod
def send(self, message: str) -> None:
pass
class EmailSender(Sender):
def send(self, message: str) -> None:
print(f"发送邮件:{message}")
Protocol 示例
python
from typing import Protocol
class Sender(Protocol):
def send(self, message: str) -> None:
...
class EmailSender:
def send(self, message: str) -> None:
print(f"发送邮件:{message}")
def notify(sender: Sender):
sender.send("你好")
在 Protocol 例子中,EmailSender 没有继承 Sender,但只要它实现了 send() 方法,静态类型检查器就会认为它符合 Sender 协议。
17. 一个更完整的实战例子:文件存储系统
假设你要设计一个存储系统,支持:
- 本地文件存储
- 云存储
- 内存存储
它们都应该支持:
- 保存数据
- 读取数据
- 删除数据
可以这样设计:
python
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def save(self, key, value):
pass
@abstractmethod
def load(self, key):
pass
@abstractmethod
def delete(self, key):
pass
class MemoryStorage(Storage):
def __init__(self):
self._data = {}
def save(self, key, value):
self._data[key] = value
def load(self, key):
return self._data.get(key)
def delete(self, key):
self._data.pop(key, None)
class LocalFileStorage(Storage):
def save(self, key, value):
with open(key, "w", encoding="utf-8") as file:
file.write(value)
def load(self, key):
with open(key, "r", encoding="utf-8") as file:
return file.read()
def delete(self, key):
import os
if os.path.exists(key):
os.remove(key)
def backup(storage: Storage, key, value):
storage.save(key, value)
print("保存成功")
print("读取结果:", storage.load(key))
memory_storage = MemoryStorage()
backup(memory_storage, "username", "Alice")
file_storage = LocalFileStorage()
backup(file_storage, "user.txt", "Bob")
这个设计的好处是:
backup()不依赖具体存储类型。- 新增存储类型时,只需要继承
Storage并实现方法。 - 如果漏实现方法,Python 会直接报错。
- 代码结构清晰,扩展性更强。
18. 常见错误和注意事项
18.1 忘记继承 ABC
有时你可能写了 @abstractmethod,但没有继承 ABC。
python
from abc import abstractmethod
class Animal:
@abstractmethod
def speak(self):
pass
这不是推荐写法。
更规范的是:
python
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
18.2 子类方法名写错
python
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speek(self):
return "汪汪"
dog = Dog()
这里子类写成了:
python
def speek(self):
而不是:
python
def speak(self):
所以会报错:
python
TypeError: Can't instantiate abstract class Dog with abstract method speak
ABC 可以帮助你提前发现这种问题。
18.3 抽象属性装饰器顺序
推荐写法:
python
@property
@abstractmethod
def name(self):
pass
完整例子:
python
from abc import ABC, abstractmethod
class User(ABC):
@property
@abstractmethod
def name(self):
pass
19. 什么时候应该使用 ABC?
适合使用 ABC 的场景包括:
- 你在写框架或库。
- 你需要定义插件接口。
- 多个类有共同能力,并且必须实现某些方法。
- 希望子类漏实现方法时尽早报错。
- 希望使用
isinstance()或issubclass()做接口判断。 - 希望基类提供部分公共逻辑,同时要求子类补充关键实现。
例如:
python
class DatabaseDriver(ABC):
@abstractmethod
def connect(self):
pass
@abstractmethod
def execute(self, sql):
pass
@abstractmethod
def close(self):
pass
这类场景很适合 ABC。
20. 什么时候不一定需要 ABC?
如果项目很小,或者只是临时写几个类,ABC 可能显得有点重。
比如:
python
def print_name(obj):
print(obj.name)
只要传进来的对象有 name 属性即可,不一定非要定义一个抽象基类。
Python 中很多时候直接使用鸭子类型就足够了。
21. 核心总结
Python 的 ABC 是一种用于定义接口规范的机制。
它主要解决的问题是:
- 规定子类必须实现哪些方法
- 防止不完整的类被实例化
- 提升大型项目中的代码可读性和可维护性
- 支持运行时类型判断
- 提供部分公共逻辑,让子类复用
最常用写法是:
python
from abc import ABC, abstractmethod
class BaseClass(ABC):
@abstractmethod
def required_method(self):
pass
class ChildClass(BaseClass):
def required_method(self):
print("实现抽象方法")
一句话记忆:
ABC 就是 Python 中用来定义"子类必须遵守的接口契约"的工具。