覆盖继承的属性设置器

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 而言,TheoremNode 之间的唯一区别是Theoremimportance 必须至少为3

定理如何继承 importance 设置器,但添加importance &gt;= 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!

在我看来,这表示:

TheoremNode 之间的唯一区别是 importance 关心的是,Theorem 必须有一个至少为3importance

比重写属性设置器要清楚得多。


请注意,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 你似乎是对的,我不明白为什么第一次没有发生这种情况!

以上是关于覆盖继承的属性设置器的主要内容,如果未能解决你的问题,请参考以下文章

iOS Objective C 覆盖属性设置器

ES5 寄生式继承

继承性和层叠性

对象属性的增删改查特性已经序列化

Python类设置器 - 每次创建新实例时都会重新分配类属性吗?

在Python中覆盖继承属性的getter和setter