abc.ABC不能直接实例化,因为其元类ABCMeta会在实例化时检查所有@abstractmethod方法是否被子类实现,未实现则抛TypeError;@abstractmethod仅在抽象基类中修饰实例/类/静态方法及property getter时有效。

为什么 abc.ABC 不能直接实例化
因为 abc.ABC 是为强制子类实现特定方法而设计的,Python 在创建实例时会检查所有标记为 @abstractmethod 的方法是否已被重写。只要有一个没实现,就会抛出 TypeError: Can't instantiate abstract class ... with abstract method ...。
常见错误是只继承 ABC 却忘了加 @abstractmethod,结果类仍可实例化——这说明抽象约束根本没生效。正确做法是:显式使用 @abstractmethod 标记至少一个方法,或让类继承自 abc.ABC 并声明 __metaclass__ = abc.ABCMeta(Python 2 风格,已过时)。
- Python 3.4+ 推荐写法:
class Shape(abc.ABC): - 错误写法:
class Shape: pass(哪怕导入了abc,也不构成抽象约束) - 注意:
abc.ABC本身只是个带ABCMeta元类的空类,真正起作用的是元类行为
@abstractmethod 必须用在哪些位置才有效
它只能修饰实例方法、类方法、静态方法和属性的 getter(即 @property),且必须出现在抽象基类定义中。如果误用在普通函数、模块级函数或实例上,会静默失效;如果放在子类里再试图“覆盖抽象方法”,也不会触发检查——抽象性只在父类定义时确立。
典型误用场景:把 @abstractmethod 加在 __init__ 上。这会导致子类无法构造实例(除非也写一个抽象 __init__),但通常你只想约束接口行为,而非初始化逻辑。应避免。
- ✅ 有效:
@abstractmethod+def area(self): - ✅ 有效:
@classmethod+@abstractmethod+def from_json(cls, data): - ❌ 无效:
def __init__(self):上加@abstractmethod(极少需要) - ❌ 无效:
@abstractmethod写在子类方法上(不改变父类抽象性)
如何让非继承关系的类“声称”实现了某个抽象基类
靠 register() 方法。它不改变类的 MRO(方法解析顺序),也不触发任何类型检查逻辑,只是把该类加入抽象基类的“虚拟子类”集合,使 isinstance(obj, MyABC) 和 issubclass(C, MyABC) 返回 True。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
适用场景:你无法修改第三方类源码,但想把它当作某接口使用(比如把 dict 当作某种 MappingLike 接口的实现)。但要注意:注册不会验证方法是否存在,运行时调用缺失方法仍会报错。
- 注册写法:
MyInterface.register(dict) - 之后:
isinstance({}, MyInterface)→True - ⚠️ 风险:注册后若调用未实现的
required_method(),仍抛AttributeError,不会提前拦截 - 替代方案:用
typing.Protocol(Python 3.8+)做结构化协议检查,更贴近鸭子类型
抽象基类与 typing.Protocol 的关键区别在哪
abc.ABC 是“名义类型”(nominal typing):必须显式继承或注册;Protocol 是“结构类型”(structural typing):只要对象有对应方法签名,就视为兼容,无需任何继承关系。
这意味着:如果你追求运行时强制约束(比如确保所有子类都提供 .serialize() 方法),用 ABC;如果你只是做类型提示、静态检查(如 mypy),且希望兼容已有类,用 Protocol 更轻量。
-
ABC支持运行时检查:issubclass(MyClass, Serializable) -
Protocol不支持运行时检查(isinstance返回False),仅用于类型检查器 - 性能影响:两者都几乎无运行时开销,但
ABC的元类机制略重于纯注解 - 混合使用可行:可以定义
class Serializable(Protocol): ...做类型提示,再另建class SerializableABC(abc.ABC): ...做运行时契约
抽象基类真正的难点不在语法,而在判断「何时该用 ABC,何时该用 Protocol,何时干脆不用」——接口设计意图比装饰器写法重要得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










