关于python:重写继承的属性setter

Overriding an inherited property setter

我有一个名为Node的类,它有一个importance setter和getter,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
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之间的唯一区别是Theorem必须具有至少3importance

一个定理如何继承importance setter,但是添加了importance >= 3的附加约束?

我试着这样做:

1
2
3
4
5
6
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类引用现有属性,并使用属性的setter方法从中创建新属性:

1
2
3
4
5
6
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返回一个带有自定义setter的新属性,该属性通常只替换旧属性。

您可以通过阅读此答案(以及问题)了解有关属性如何工作的更多信息。


一种方法是使用Node getter在Theorem上实现一个新属性,提供一个新的setter方法并在其中显式调用Node setter:

1
2
3
4
5
6
7
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无法做到这一点。

根据此错误报告,您可以执行以下操作:

1
2
3
4
5
6
7
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安排的(将于2015年9月到期)。


对于更广泛的问题,这是一个完全不同的解决方案,并且不那么复杂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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!

在我看来,这表达了:

The only difference between a Theorem and a Node, as far as importance
is concerned, is that a Theorem must have an importance of at least 3.

比覆盖属性setter更清楚。

请注意,assert通常用于测试和调试,而不是作为一般程序流程的一部分,当然也不适用于您预期会发生的事情;见例如Python Assert的最佳实践。