Python抽象类多继承dict时抽象方法校验失效如何解决?
问题成因
这一现象的核心原因是 dict 属于Python C原生实现的内置类型,abc模块的抽象类校验逻辑是在Python层面的元类abc.ABCMeta中实现的,原生内置类型的实例化逻辑会绕过该校验:
- 抽象类的实例化校验本质是在元类的
__call__方法中检查类的__abstractmethods__属性是否为空,存在未实现的抽象方法时抛出TypeError - 当类同时继承了
abc.ABC和C实现的内置类型(如dict、list、tuple)时,内置类型的实例化逻辑不会触发Python层面的元类校验,因此未实现抽象方法的子类也能被正常实例化,调用未实现的抽象方法时会直接执行父类中的默认逻辑
解决方案
有两种常用方案可以同时保留抽象类强制实现的校验和字典操作能力:
方案1:使用collections.UserDict替代直接继承dict(推荐)
UserDict是纯Python实现的字典包装类,完全兼容dict的所有操作API,同时不会绕过ABC的抽象校验逻辑,修改继承对象即可:
from collections import UserDict import abc class Record(abc.ABC): # 注意:Python3.3+推荐@property搭配@abstractmethod替代废弃的@abstractproperty @property @abc.abstractmethod def date(self) -> str: print('I am abstract so should never be called') @abc.abstractmethod def is_valid(self) -> bool: print('I am abstract so should never be called') class ValidRecord(Record, UserDict): @property def date(self) -> str: return self['date'] def is_valid(self) -> bool: return self['valid'] class InvalidRecord(Record, UserDict): @property def date(self) -> str: return self['date']
此时实例化InvalidRecord会正常抛出TypeError: Can't instantiate abstract class InvalidRecord with abstract method is_valid,符合预期。
方案2:手动添加抽象校验逻辑
如果必须直接继承dict,可以在抽象基类中手动实现__new__方法主动校验抽象方法实现情况:
import abc class Record(abc.ABC): def __new__(cls, *args, **kwargs): # 手动检查是否存在未实现的抽象方法 if cls.__abstractmethods__: raise TypeError(f"Can't instantiate abstract class {cls.__name__} with abstract methods {', '.join(cls.__abstractmethods__)}") return super().__new__(cls, *args, **kwargs) @property @abc.abstractmethod def date(self) -> str: print('I am abstract so should never be called') @abc.abstractmethod def is_valid(self) -> bool: print('I am abstract so should never be called') # 直接继承dict也能触发校验 class InvalidRecord(Record, dict): @property def date(self) -> str: return self['date']
内容的提问来源于stack exchange,提问作者Richard Boyne
相关产品推荐
相关产品推荐

