【问题标题】:Acessing base class property when overriding覆盖时访问基类属性
【发布时间】:2019-10-13 02:04:06
【问题描述】:

考虑以下基本 Python 类:

class Foo(object):

    def __init__(self, a1):
        self._att1 = a1

    @property
    def att1(self):
        return self._att1

    @att1.setter
    def att1(self, a):
        try:
            self._att1 = a
        except TypeError:
            # handle exception
            print("Not allowed")

class Bar(Foo):
    def __init__(self):
        Foo.__init__(self, 100)

    @Foo.att1.setter
    def att1(self, a):
        # self.att1 = a * 0.5  # RecursionError: maximum recursion depth exceeded
        # Foo.att1 = a * 0.5  # attribute is not changed (base instances are though (?))
        # Foo.att1(self,a)  # TypeError: 'property' object is not callable
        super().att1.__set__(a * 0.5)   # AttributeError: 'int' object has no attribute '__set__'
        # ... some other additional code ...

a = Foo(5)
b = Bar()
b.att1 = 700

print(a.att1)
print(b.att1)

从子类的覆盖中调用基属性设置器的语法是什么?我知道我可以直接设置self._att1,但我想避免这种情况,因为我需要重复异常处理代码。这只是一个简单的例子,我有更复杂的情况,基类在属性上实现了一些额外的操作,我想避免在派生类属性设置器上重复相同的代码。

【问题讨论】:

  • 我认为self._att1 = 是个不错的主意。
  • 对不起,应该是Foo.att1.__set__(a)
  • Foo.att1.__set__(self, a * 0.5)
  • @rdas 是的。 @jonrsharpe __set__ 抱怨它也需要 self 参数。 @martineau 我编辑了帖子以包含该尝试
  • @jonrsharpe:我的意思是 OP 在两个类定义之外执行一些代码,我想知道它是什么。

标签: python class properties attributes overriding


【解决方案1】:

代码:

class Foo:
    def __init__(self, a1):
        self._att1 = a1

    @property
    def att1(self):
        return self._att1

    @att1.setter
    def att1(self, a):
        if not isinstance(a, int):
            print("Not allowed")
        else:
            self._att1 = a


class Bar(Foo):
    def __init__(self):
        Foo.__init__(self, 100)

    @Foo.att1.setter
    def att1(self, a):
        Foo.att1.fset(self, a * 2)


c = Bar()
print(c.att1)
c.att1 = 10
print(c.att1)
c.att1 = "some string"

输出:

100
20
Not allowed

UPD。

根据@chepner 的建议,我决定添加一些解释。

当您使用装饰器 @Foo.att1.setter 时,它不会像您预期的那样工作。

docs 中,您可以看到两个声明属性的示例:使用property() 函数分配类变量和使用@property 装饰器。这两种方法都是等效的,但我首先发现在演示提供的代码如何工作时更加明显。

让我们使用property() 函数而不是装饰器来重写类声明:

class Foo:
    def __init__(self, a1):
        self._att1 = a1

    def getatt1(self):
        return self._att1

    def setatt1(self, a):
        if not isinstance(a, int):
            print("Not allowed")
        else:
            self._att1 = a

    att1 = property(getatt1, setatt1)

class Bar(Foo):
    def __init__(self):
        super().__init__(100)

    def setatt1(self, a):
        Foo.att1.fset(self, a * 2)

    att1 = property(Foo.getatt1, setatt1)

如您所见,您没有覆盖属性,而是创建了具有相同名称的新属性,它隐藏了基类属性。您可以使用以下代码证明它:

print(f"Foo.att1 is Bar.att1: {Foo.att1 is Bar.att1}")

在这两个声明中,它都会返回False,这意味着这些类的属性对象不一样。

【讨论】:

  • 我认为要注意的关键是您没有覆盖Bar中的方法;你正在创建一个全新的属性Bar.att1 通过传递Foo.att1.setter 一个新的setter 方法。在该方法中,您必须访问原始 property 的属性,而不是 Foo
  • @chepner:为什么您认为这是需要注意的关键点?
  • 因为这里涉及到继承的唯一方式是Foo有一个名为att1的属性;你没有覆盖Foo 的任何方法。 att1.setter 不返回函数;它返回property 的新实例。该实例具有各种函数属性,其中之一是fset,它从att1.__set__ 调用。新属性中fset 的值与原始属性中fset 的值不同。
  • @chepner:好吧,也许我还没有喝足够的咖啡,但这听起来像是人们想要的......无论如何,我认为使用Foo.att1.__set__()而不是Foo.att1.fset()会是一种更好的编码方式,因为前者已记录在案。
  • @martineau,fsetdocumented。引用自文档:The returned property object also has the attributes fget, fset, and fdel corresponding to the constructor arguments.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-05
  • 1970-01-01
  • 1970-01-01
  • 2015-09-30
  • 2018-12-10
  • 1970-01-01
相关资源
最近更新 更多