继承type与typing.Mapping时触发__subclasses__ TypeError问题求助
解决同时继承type与Mapping的元类引发的TypeError问题
解决方案
要解决这个问题,需重写__subclasscheck__方法以规避ABC(抽象基类)与type的方法冲突,同时实现Mapping要求的抽象方法来满足接口规范:
import dataclasses from collections.abc import Mapping class MyDataclassMeta(type, Mapping): # 实现Mapping的抽象方法(可根据业务需求自定义逻辑) def __getitem__(self, key): return getattr(self, key) def __len__(self): return len([attr for attr in dir(self) if not attr.startswith('__')]) def __iter__(self): return iter([attr for attr in dir(self) if not attr.startswith('__')]) # 重写__subclasscheck__,分离type与Mapping的子类检查逻辑 def __subclasscheck__(cls, subclass): # 先检查是否符合type的子类关系 if type.__subclasscheck__(cls, subclass): return True # 再检查是否符合Mapping的子类要求 return Mapping.__subclasscheck__(cls, subclass)
验证代码
修改后的元类可正常用于dataclass:
@dataclasses.dataclass class MyDataclass(metaclass=MyDataclassMeta): name: str age: int
也能正常处理Mapping虚拟子类的注册:
@Mapping.register class CompletelyUnrelatedClass: def __getitem__(self, key): pass def __len__(self): pass def __iter__(self): pass
问题根源
当类同时继承type和Mapping(Mapping继承自ABC)时,ABC的默认__subclasscheck__逻辑会递归触发子类检查,但type的__subclasses__方法需要绑定到实例调用,在ABC的检查流程中被当作未绑定方法调用,因此抛出TypeError: descriptor '__subclasses__' of 'type' object needs an argument。
重写__subclasscheck__后,我们显式拆分type和Mapping的子类检查逻辑,避免了递归调用和方法绑定错误。
原始问题与复现
需求
定义一个同时具备以下两个功能的类:
- 作为dataclass的元类(继承
type) - 具备映射(Mapping)行为(继承
typing.Mapping)
最小复现代码
import dataclasses, typing class MyDataclassMeta(type, typing.Mapping): # 可在此实现Mapping的抽象方法(与报错无关) pass # 取消注释触发报错1 # @dataclasses.dataclass # class MyDataclass(metaclass=MyDataclassMeta): # pass # 取消注释触发报错2 # @typing.Mapping.register # class CompletelyUnrelatedClass: # pass if __name__ == "__main__": pass
报错信息
报错1(用作dataclass元类时)
Traceback (most recent call last): File "foo.py", line 10, in <module> class MyDataclass(metaclass=MyDataclassMeta): File "D:\Python38\lib\dataclasses.py", line 1019, in dataclass return wrap(cls) File "D:\Python38\lib\dataclasses.py", line 1011, in wrap return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen) File "D:\Python38\lib\dataclasses.py", line 991, in _process_class str(inspect.signature(cls)).replace(' -> None', '')) File "D:\Python38\lib\inspect.py", line 3105, in signature return Signature.from_callable(obj, follow_wrapped=follow_wrapped) File "D:\Python38\lib\inspect.py", line 2854, in from_callable return _signature_from_callable(obj, sigcls=cls, File "D:\Python38\lib\inspect.py", line 2307, in _signature_from_callable if _signature_is_builtin(obj): File "D:\Python38\lib\inspect.py", line 1847, in _signature_is_builtin obj in (type, object)) File "D:\Python38\lib\_collections_abc.py", line 685, in __eq__ if not isinstance(other, Mapping): File "D:\Python38\lib\abc.py", line 98, in __instancecheck__ return _abc_instancecheck(cls, instance) File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__ return _abc_subclasscheck(cls, subclass) File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__ return _abc_subclasscheck(cls, subclass) TypeError: descriptor '__subclasses__' of 'type' object needs an argument
报错2(注册Mapping虚拟子类时)
Traceback (most recent call last): File "foo.py", line 15, in <module> class CompletelyUnrelatedClass: File "D:\Python38\lib\abc.py", line 94, in register return _abc_register(cls, subclass) File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__ return _abc_subclasscheck(cls, subclass) File "D:\Python38\lib\abc.py", line 102, in __subclasscheck__ return _abc_subclasscheck(cls, subclass) TypeError: descriptor '__subclasses__' of 'type' object needs an argument
内容的提问来源于stack exchange,提问作者joxirerehufe
相关产品推荐
相关产品推荐

