直接回答:abc 是名义子类型(nominal subtyping):子类必须显式继承(或通过 register() 注册)抽象基类,抽象方法未实现时实例化直接报 TypeError,检查发生在运行时。Protocol(PEP 544)是结构子类型(structural subtyping):对象只要具备协议要求的方法和属性签名就算满足,无需继承任何东西,主要服务于 mypy、pyright 等静态检查器,是"类型化的鸭子类型"。

from typing import Protocol

class SupportsClose(Protocol):
    def close(self) -> None: ...

def cleanup(res: SupportsClose) -> None:
    res.close()          # 任何有 close() 方法的对象都能传入

展开解析:关键差异有四。继承关系上,abc 侵入性强,用户必须知道并继承你的基类;Protocol 零侵入,第三方库里的类也能天然满足。运行时行为上,Protocol 默认不支持 isinstance,加 @runtime_checkable 后可以,但只检查方法是否存在、不校验签名,且检查要遍历 MRO,高频调用有开销。能力上,abc 可以携带具体实现,适合做模板方法模式的骨架;Protocol 只描述接口形状。生态上,标准库的 collections.abc.Iterable 等老牌 ABC 两种角色都能扮演。

实践要点:框架内部、需要共享实现或强制继承关系时用 abc;写公共库、希望用户的自定义类型以鸭子类型方式接入时,参数标注 Protocol 是对调用方最友好的做法。两者可以混用:内部用 abc 保证骨架和默认实现,对外 API 的参数类型暴露 Protocol。

追问方向:Protocol 能声明非方法成员(属性)吗?为什么 @runtime_checkable 的 isinstance 不检查方法签名?

(约 540 字)