__subclasscheck__ 不在 isinstance 中触发,因其仅影响 issubclass();isinstance 依赖 __mro__ 和继承链,需实现 __instancecheck__ 才能自定义逻辑。

为什么 __subclasscheck__ 不会在普通 isinstance 中触发?
它只影响 issubclass(),不参与 isinstance() 的类型判断。很多开发者误以为加了这个方法就能让 isinstance(obj, MyABC) 按自定义逻辑走,结果发现完全没生效——因为 isinstance 查的是 __mro__ 和实际继承链,除非你同时实现 __instancecheck__,否则它根本不看 __subclasscheck__。
典型错误现象:issubclass(SomeClass, MyABC) 返回 False,即使你在 MyABC.__subclasscheck__ 里写了 return True —— 那是因为 MyABC 没有继承 abc.ABC 或没被正确注册为抽象基类。
- 必须让类显式继承
abc.ABC,或通过MyABC.register(OtherClass)注册 -
__subclasscheck__只在issubclass(cls, ABCSubclass)调用时执行,参数cls是被检查的类对象(不是实例) - 若未定义该方法,Python 默认走 MRO 线性搜索;一旦定义,就完全接管逻辑,包括跳过父类检查
如何让 issubclass 接受鸭子类型类?
比如你想把任何实现了 .read() 和 .close() 方法的类都当作“类文件对象”,不用真继承 io.IOBase。这时可以在自定义 ABC 中重写 __subclasscheck__,动态检查属性和可调用性。
import abc <p>class FileLike(abc.ABC): def <strong>subclasscheck</strong>(cls, subclass): return ( hasattr(subclass, 'read') and callable(getattr(subclass, 'read')) and hasattr(subclass, 'close') and callable(getattr(subclass, 'close')) )</p><h1>测试类(没继承,也没注册)</h1><p>class FakeFile: def read(self): return b'' def close(self): pass</p><p>print(issubclass(FakeFile, FileLike)) # True </p>
注意:这种检查只发生在类层级,不涉及实例状态;且不会缓存结果,每次 issubclass 都会重新执行该函数,高频调用可能有性能影响。
- 避免在
__subclasscheck__中做耗时操作(如 import、网络请求、磁盘读取) - 不要依赖实例属性或
self—— 这个方法没有self参数,第一个参数是类本身(cls),第二个才是待检查的subclass - 如果检查逻辑复杂,建议提取成独立函数,便于单元测试
__subclasscheck__ 和 register() 冲突怎么办?
当你既调用了 MyABC.register(SomeClass),又实现了 __subclasscheck__,Python 会优先走 __subclasscheck__ 的返回值,register 的注册记录会被绕过。这容易导致预期外的行为:比如你注册了一个类,但 issubclass 却返回 False,只因自定义逻辑里漏判了它。
- 若需保留
register效果,应在__subclasscheck__开头手动检查subclass in cls._registry(但_registry是私有属性,不推荐) - 更稳妥的做法是:只用
register做静态注册,不用__subclasscheck__;或者彻底放弃register,全由__subclasscheck__控制 - CPython 中
abc.ABCMeta.__subclasscheck__的默认实现会先查_abc_registry,再查 MRO;你覆盖后就得自己模拟这套逻辑,否则就丢掉了 ABC 的原始语义
真正要注意的边界情况
最常被忽略的一点:如果 __subclasscheck__ 抛出异常,issubclass 会直接报错,而不是静默回退到默认逻辑。这意味着你不能假设“检查失败就等于 False”——它可能是崩溃。
- 务必用
try/except包裹可能出错的反射操作(如getattr、inspect.signature) - 不要在其中修改类结构(如动态添加父类、改
__bases__),会导致元类状态不一致 - 多继承场景下,若多个 ABC 都定义了
__subclasscheck__,Python 不会合并它们,而是按 MRO 顺序取第一个能处理的——这点文档极少提,但实测如此
自定义 __subclasscheck__ 是元编程里的窄门,用对了很灵活,用错了连错误都难定位。它不解决“怎么让类看起来像某种类型”的通用问题,只解决“怎么让 issubclass 返回我想要的布尔值”这一个具体问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











