Python中的虚拟子类机制
# 观察如下代码
from collections.abc import Iterable, Sequence
a = []
isinstance(a, list) # True
isinstance(a, Iterable) # True
isinstance(a, Sequence) # True
list.__mro__ # (<class 'list'>, <class 'object'>)
1. 问题
在上述代码中,a
既是list
的实例,又是collections.abc.Iterable
的实例,同时还是collections.abc.Sequence
的实例,为什么list
的类继承关系(通过查看类的mro()
方法或__mro__
属性得到,注意是类而不是类的实例,a
并无mro()
方法或__mro__
属性)上并没有<class 'collections.abc.Iterable'>
和<class 'collections.abc.Sequence'>
呢?
2. 说明
这是一个关于Python抽象基类(Abstract Base Classes, ABCs)和鸭子类型(duck typing)的问题。关键在于Python的isinstance()
检查不仅仅看继承关系,还会考虑注册机制和抽象方法实现。在Python中,list
类并没有显式继承自collections.abc.Iterable
和collections.abc.Sequence
,但它的实例依然被isinstance
判定为Iterable
和Sequence
的实例。这是因为:
- list的隐式协议实现
Python的list
类内置了Iterable
要求的__iter__
方法(使其可迭代),以及Sequence
要求的__getitem__()
和__len__()
方法。因此自动满足Iterable
和Sequence
的接口要求。isinstance
检查通过协议(方法存在性)而非继承关系判断类型,只要一个类实现了这些方法,isinstance()
就会认为它是这些ABC的实例(即使没有显式继承)。
from collections.abc import Sequence, Iterable
class A:
def __iter__(self):
pass
a = A()
print(isinstance(a, Iterable)) # True
- 抽象基类(ABC)的虚拟子类机制
list
并没有直接继承collections.abc.Iterable
或collections.abc.Sequence
,但它通过Python的抽象基类注册机制被隐式认为是这些ABC的子类。抽象基类可以通过register()
方法将其他类强行注册为自己的“虚拟子类”,即使它们没有直接继承关系也没有实现自己要求的方法。
from collections.abc import Sequence, Iterable
class A:
pass
Iterable.register(A)
a = A()
print(isinstance(a, Iterable)) # True
class B:
pass
Sequence.register(B)
b = B()
print(isinstance(b, Sequence)) # True
- MRO仅显示显式继承链
list.__mro__
只展示类的显式继承关系(即直接父类),而虚拟子类是通过ABC机制动态认可的,不会修改类的__mro__
。由于list
未显式继承Iterable
和Sequence
,故Iterable
和Sequence
不会出现在MRO中。
3.具体验证
查看注册关系:
from collections.abc import Sequence
print(Sequence.__subclasses__()) # 查看直接子类
print(Sequence._abc_registry) # 查看通过register()注册的虚拟子类
虽然list
不会出现在__subclasses__()
中(因为它不是直接子类),但ABC会通过_abc_registry
或抽象方法检查认可它。
方法实现检查:
Iterable
要求实现__iter__
,list
有。Sequence
要求实现__getitem__
和__len__
,list
也有。
4. 总结
isinstance(a, Iterable)
为True
是因为list
实现了__iter__
,触发了虚拟子类机制,Sequence
也是类似情况。list.__mro__
不包含Iterable
和Sequence
,因为虚拟子类不修改显式继承关系。isinstance()
对ABC的检查是基于方法实现或注册关系,而非严格的继承关系。- 这种设计是Python鸭子类型的核心:只要“像鸭子”(实现所需方法),就会被视为鸭子(通过
isinstance
检查)。 collections.abc
中的ABC本质上是为isinstance()
提供更语义化的类型检查,而非强制继承。
这种设计使得Python的类型系统更灵活——类只需实现协议(方法),无需显式继承抽象基类。