覆盖继承的属性设置器
Posted
技术标签:
【中文标题】覆盖继承的属性设置器【英文标题】:Overriding an inherited property setter 【发布时间】:2015-11-01 17:59:47 【问题描述】:我有一个名为Node
的类,它有一个importance
setter 和getter,如下所示:
class Node:
@property
def importance(self):
return self._importance
@importance.setter
def importance(self, new_importance):
if new_importance is not None:
new_importance = check_type_and_clean(new_importance, int)
assert new_importance >= 1 and new_importance <= 10
self._importance = new_importance
稍后,我有一个继承自 Node
的类 Theorem
。就importance
而言,Theorem
和Node
之间的唯一区别是Theorem
的importance
必须至少为3
。
定理如何继承 importance
设置器,但添加importance >= 3
的附加约束?
我试着这样做:
class Theorem(Node):
@importance.setter
def importance(self, new_importance):
self.importance = new_importance # hoping this would use the super() setter
assert self.importance >= 3
【问题讨论】:
请注意,您可以通过在Node
中使用 1
和在 Theorem
中使用 3
的类属性 MIN_IMPORTANCE
来完全不同地解决问题。
Overriding properties in python的可能重复
【参考方案1】:
对于更广泛的问题,这是一个完全不同的解决方案,而且少了很多麻烦:
class Node:
MIN_IMPORTANCE = 1
MAX_IMPORTANCE = 10
@property
def importance(self):
return self._importance
@importance.setter
def importance(self, new_importance):
if new_importance is not None:
new_importance = check_type_and_clean(new_importance, int)
assert (new_importance >= self.MIN_IMPORTANCE and
new_importance <= self.MAX_IMPORTANCE)
self._importance = new_importance
class Theorem(Node):
MIN_IMPORTANCE = 3
# and that's all it takes!
在我看来,这表示:
Theorem
和Node
之间的唯一区别是importance
关心的是,Theorem
必须有一个至少为3
的importance
。
比重写属性设置器要清楚得多。
请注意,assert
通常用于测试和调试,而不是作为一般程序流程的一部分,当然也不用于您期望可能发生的事情;参见例如Best practice for Python Assert.
【讨论】:
这是针对提出的具体示例问题的最佳解决方案。上面接受的答案是接受的答案,因为它解决了所提出的问题并且与标题有关。不过,我很欣赏这个深思熟虑的答案。【参考方案2】:您可以直接通过Node
类引用现有的属性,并使用属性的setter
方法从它创建一个新的属性:
class Theorem(Node):
@Node.importance.setter
def importance(self, new_importance):
# You can change the order of these two lines:
assert new_importance >= 3
Node.importance.fset(self, new_importance)
这将在Theorem
类中创建一个新属性,该属性使用来自Node.importance
的getter 方法,但用不同的方法替换setter 方法。
这就是属性的一般工作方式:调用属性的 setter
会返回一个带有自定义设置器的新属性,它通常只是替换旧属性。
您可以通过阅读this answer(以及问题)了解更多关于属性如何工作的信息。
【讨论】:
TypeError: setter() takes exactly one argument (2 given)
。你的意思是fset
?
@jonrsharpe Ups,立即尝试:P
是的,比我的好得多,也更整洁!
@MarkusMeskanen 所以在importance
的定义之上写@Node.importance.setter
与在重要性定义之下写importance = Node.importance.setter(importance)
是一回事吗?
@mareoraft 确实是 all 装饰器的工作方式,@
只是语法糖。【参考方案3】:
一种方法是使用Node
getter 在Theorem
上实现一个新属性,提供一个新的setter 方法并在其中显式调用Node
setter:
class Theorem(Node):
def _set_importance(self, new):
Node.importance.fset(self, new)
assert self.importance >= 3
importance = property(Node.importance.fget, _set_importance)
据我所知,super
无法做到这一点。
根据this bug report,您可以这样做:
class Theorem(Node):
def _set_importance(self, new):
super(Theorem, Theorem).importance.fset(self, new)
assert self.importance >= 3
importance = property(Node.importance.fget, _set_importance)
但是,这显然有点尴尬;允许super()
的补丁似乎是为 Python 3.5 安排的(在September 2015 到期)。
【讨论】:
不要将self.__class__
传递给super()
。当您从 Theorem
继承时,它会完全崩溃。
@Kevin 会吗?快速测试表明它从Theorem
子类实例上升到Node.importance.fset
。
ISTM super(TheoremSubclass, TheoremSubclass).importance
将返回您在最后一行创建的对象。一旦你调用fset()
,那应该会产生无限递归。
@Kevin 你似乎是对的,我不明白为什么第一次没有发生这种情况!以上是关于覆盖继承的属性设置器的主要内容,如果未能解决你的问题,请参考以下文章