如何在Cython扩展类型的子类中重写属性setter?
Cython cdef类中重写属性setter的解决方案
问题背景
原本的Python代码中,父类A通过@property定义了带getter和setter的属性x,子类B通过@A.x.setter重写该setter,在纯Python环境和普通Cython类中均可正常运行:
import abc class A: __metaclass__ = abc.ABCMeta def __init__(self): self.__x = 0 @property def x(self): return self.__x @x.setter def x(self, newX): print("Setter in class A") self.__x = newX class B(A): @A.x.setter def x(self, newX): print("Setter in class B") A.x.fset(self, newX) b = B() b.x = 42 print(b.x)
运行输出:
Setter in class B Setter in class A 42
但将A改为Cython扩展类型(cdef class A)后,编译通过但运行时抛出异常:
File "test.pyx", line 21, in init test @A.x.setter AttributeError: 'getset_descriptor' object has no attribute 'setter'
这是因为Cython的cdef类属性是getset_descriptor类型,不支持普通Python类@property的.setter装饰器语法。
可行解决方案
方案1:在子类中重新定义@property并调用父类的descriptor方法
在子类B中重新实现@property,getter部分直接调用父类A的属性get方法,setter部分调用父类的属性set方法:
import abc cdef class A: __metaclass__ = abc.ABCMeta cdef int __x def __init__(self): self.__x = 0 @property def x(self): return self.__x @x.setter def x(self, newX): print("Setter in class A") self.__x = newX class B(A): @property def x(self): # 调用父类A的x属性的get方法 return A.x.__get__(self) @x.setter def x(self, newX): print("Setter in class B") # 调用父类A的x属性的set方法 A.x.__set__(self, newX) b = B() b.x = 42 print(b.x)
方案2:将父类的setter逻辑抽离为单独方法,子类重写该方法
在父类A中把setter的核心逻辑抽离成一个普通方法,让子类重写这个方法,而@x.setter仅作为入口调用该方法:
import abc cdef class A: __metaclass__ = abc.ABCMeta cdef int __x def __init__(self): self.__x = 0 @property def x(self): return self.__x def _set_x(self, newX): print("Setter in class A") self.__x = newX @x.setter def x(self, newX): self._set_x(newX) class B(A): def _set_x(self, newX): print("Setter in class B") # 调用父类的_set_x方法 super()._set_x(newX) b = B() b.x = 42 print(b.x)
两种方案运行后均能得到预期输出:
Setter in class B Setter in class A 42
内容的提问来源于stack exchange,提问作者Daniel Herding
相关产品推荐
相关产品推荐

