Python中能否同时重写抽象属性的get/set访问器?
抽象类中重写@property的getter和setter问题
问题描述
尝试使用abc模块定义包含抽象getter和setter的抽象类C,子类D重写这两个访问器后,运行结果显示D.x的getter仍使用C的抽象实现。请问是写法有误,还是无法同时重写这两个抽象访问器?
代码示例
from abc import ABC, abstractmethod class C(ABC): @property @abstractmethod def x(self): pass @x.setter @abstractmethod def x(self, val): pass class D(C): @C.x.getter def x(self): pass @C.x.setter def x(self, val): pass def examine_property(p): print('get', p.fget, getattr(p.fget, '__isabstractmethod__', False) if p.fget is not None else None) print('set', p.fset, getattr(p.fset, '__isabstractmethod__', False) if p.fset is not None else None) print('del', p.fdel, getattr(p.fdel, '__isabstractmethod__', False) if p.fdel is not None else None) print("C.x:") examine_property(C.x) print("D.x:") examine_property(D.x)
运行输出
C.x: get <function C.x at 0x1017af2e0> True set <function C.x at 0x1017af380> True del None None D.x: get <function C.x at 0x1017af2e0> True set <function D.x at 0x1017af4c0> False del None None
原因分析与解决方法
问题根源
你在子类D中使用@C.x.getter的写法是错误的。C.x是父类的property对象,调用它的getter方法生成新属性时,并没有正确替换父类的抽象getter实现——实际上这个操作复用了父类C中x的getter方法,导致D.x的getter仍然指向父类的抽象方法。
正确写法
在子类中重写带抽象访问器的property时,需要先通过@property装饰器定义子类自己的getter方法,再用@x.setter来定义setter方法,这样才能完全覆盖父类的抽象访问器。
修正后的代码如下:
from abc import ABC, abstractmethod class C(ABC): @property @abstractmethod def x(self): pass @x.setter @abstractmethod def x(self, val): pass class D(C): @property def x(self): # 子类自定义getter实现 pass @x.setter def x(self, val): # 子类自定义setter实现 pass def examine_property(p): print('get', p.fget, getattr(p.fget, '__isabstractmethod__', False) if p.fget is not None else None) print('set', p.fset, getattr(p.fset, '__isabstractmethod__', False) if p.fset is not None else None) print('del', p.fdel, getattr(p.fdel, '__isabstractmethod__', False) if p.fdel is not None else None) print("C.x:") examine_property(C.x) print("D.x:") examine_property(D.x)
修正后的运行输出
C.x: get <function C.x at 0x1020b72e0> True set <function C.x at 0x1020b7380> True del None None D.x: get <function D.x at 0x1020b74c0> False set <function D.x at 0x1020b7560> False del None None
可以看到,D.x的getter和setter都已经替换为子类自己的实现,__isabstractmethod__均为False,符合预期。
内容的提问来源于stack exchange,提问作者М. Ю. Брызгалов
相关产品推荐
相关产品推荐

