You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:58:11