Python 3.x:应该如何覆盖从父类继承的属性?

问题描述 投票:0回答:1

一个简单的例子可能会显示更多:

class NaturalNumber():
    def __init__(self, val):
        self._value = val

    def _get_value(self):
        return self._value

    def _set_value(self, val):
        if val < 0:
            raise ValueError(
                f"Cannot set value to {val}: Natural numbers are not negative."
            )
        self._value = val

    value = property(_get_value, _set_value, None, None)


class EvenNaturalNumber(NaturalNumber):
    # This seems superfluous but is required to define the property.
    def _get_value(self):
        return super()._get_value()

    def _set_value(self, val):
        if val % 2:
            raise ValueError(
                f"Cannot set value to {val}: Even numbers are divisible by 2."
            )
        super()._set_value(val)

    # This seems superfluous but parent property defined with parent setter.
    value = property(_get_value, _set_value, None, None)

这是来自真实用例的简化示例,我想在其中注入代码以对生产类进行测试。原理是一样的,这里我在继承于

value
类的
EvenNaturalNumber
类中对
NaturalNumber
进行额外的验证。我的老板不喜欢我摆脱他的所有装饰器,所以理想情况下,无论底层类是如何编写的,解决方案都应该有效。

看起来很自然的是:

class NaturalNumber():
    def __init__(self, val):
        self._value = val
    
    @property
    def value(self):
        return self._value
    
    @value.setter
    def value(self, val):
        if val < 0:
            raise ValueError(
                f"Cannot set value to {val}: Natural numbers are not negative."
            )
        self._value = val


class EvenNaturalNumber(NaturalNumber):
    @property
    def value(self):
        return super().value
    
    @value.setter
    def value(self, val):
        if val % 2:
            raise ValueError(
                f"Cannot set value to {val}: Even numbers are divisible by 2."
            )
        super().value = val

但是这个错误与 EvenNaturalNumber.value 上的有效集(例如,= 2)有关。与

AttributeError: 'super' object has no attribute 'value'

就我个人而言,我会说这是 python 语言中的一个错误!但我怀疑我错过了什么。


我找到了使用装饰器的解决方案,但这似乎相当复杂:

class NaturalNumber():
    def __init__(self, val):
        self._value = val

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, val):
        if val < 0:
            raise ValueError(
                f"Cannot set value to {val}: Natural numbers are not negative."
            )
        self._value = val


class EvenNaturalNumber(NaturalNumber):
    @property
    def value(self):
        return super().value
    
    @value.setter
    def value(self, val):
        if val % 2:
            raise ValueError(
                f"Cannot set value to {val}: Even numbers are divisible by 2."
            )
        super(type(self), type(self)).value.fset(self, val)

另一种方法是:

class NaturalNumber():
    def __init__(self, val):
        self._value = val
    
    @property
    def value(self):
        return self._value
    
    @value.setter
    def value(self, val):
        if val < 0:
            raise ValueError(
                f"Cannot set value to {val}: Natural numbers are not negative."
            )
        self._value = val


class EvenNaturalNumber(NaturalNumber):
    # This seems superfluous but is required to define the property.
    def _set_value(self, val):
        if val % 2:
            raise ValueError(
                f"Cannot set value to {val}: Even numbers are divisible by 2."
            )
        NaturalNumber.value.fset(self, val)

    # This seems superfluous as parent property defined with parent setter.
    value = NaturalNumber.value.setter(_set_value)

但是第二个解决方案似乎相当不令人满意,因为它利用了 value 属性在

NaturalNumber
类中定义的知识。我看不到任何方法来迭代
EvenNaturalNumber.__mro__
,除非我在
EvenNaturalNumber._set_value(self, val)
中执行此操作,但这就是
super()
的工作,不是吗?

如有任何改进或建议,我们将不胜感激。否则,我的老板就只能和

super(type(self), type(self)).value.fset(self, val)
一起生活了!

python-3.x inheritance multiple-inheritance super class-properties
1个回答
0
投票

我不确定为什么要在重写的属性中调用父属性。您能解释一下为什么要这样做吗?

我的意思是,这样做的正常我将是一个可以轻松覆盖和调用的辅助方法:

class NaturalNumber:
    def __init__(self, val):
        self._value = val

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, val):
        self._validate(val)
        self._value = val

    def _validate(self, val: int | float) -> None:
        if val < 0:
            raise ValueError(
                f"Cannot set value to {val}: Natural numbers are not negative."
            )


class EvenNaturalNumber(NaturalNumber):
    def _validate(self, val: int | float) -> None:
        super()._validate(val)
        if val % 2:
            raise ValueError(
                f"Cannot set value to {val}: Even numbers are divisible by 2."
            )
© www.soinside.com 2019 - 2024. All rights reserved.