一个简单的例子可能会显示更多:
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)
一起生活了!
我不确定为什么要在重写的属性中调用父属性。您能解释一下为什么要这样做吗?
我的意思是,这样做的正常我将是一个可以轻松覆盖和调用的辅助方法:
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."
)