Python中使用ABC时如何强制子类实现property的setter方法
问题原因
这是Python ABC的原生设计特性,不是遗漏装饰器导致的。
ABC的抽象属性校验仅会检查子类是否存在同名的value属性,不会深入校验该属性对应的property对象是否完整实现了父类定义的getter、setter逻辑。你在子类中仅定义带@property装饰的getter时,相当于生成了一个仅包含getter的全新property对象覆盖了父类的抽象property,ABC会认为你已经实现了value属性,因此不会抛出校验错误。
实现强制校验的方案
方案1:拆分抽象方法(推荐)
将get、set逻辑拆分为两个独立的抽象方法,父类统一封装为property,这样子类必须同时实现两个抽象方法才能实例化:
from abc import ABC, abstractmethod class AbstractDevice(ABC): # 定义独立的抽象读写方法 @abstractmethod def _get_value(self): pass @abstractmethod def _set_value(self, val): pass # 父类统一封装为property,子类无需重复定义property @property def value(self): return self._get_value() @value.setter def value(self, val): self._set_value(val) # 子类仅实现_get_value的话,实例化时会抛出缺少_set_value的错误 class Device(AbstractDevice): def _get_value(self): return 1 # 必须补全下面的方法才能正常实例化 # def _set_value(self, val): # pass
这个方案符合Python ABC的常规设计逻辑,兼容性强,无需额外校验逻辑。
方案2:子类定义时强制校验property结构
如果你希望子类直接实现property的setter,可以通过__init_subclass__钩子增加自定义校验逻辑,子类定义时就会触发检查:
from abc import ABC, abstractmethod class AbstractDevice(ABC): @property @abstractmethod def value(self): pass @value.setter @abstractmethod def value(self, val): pass def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) # 校验value属性的setter是否存在 value_prop = getattr(cls, 'value', None) if not isinstance(value_prop, property) or value_prop.fset is None: raise NotImplementedError(f"子类{cls.__name__}必须实现完整的value property,包含setter逻辑")
只要子类的value不是property或者没有实现setter,定义子类阶段就会直接抛出错误。
内容的提问来源于stack exchange,提问作者PotatoPeeler
相关产品推荐
相关产品推荐

