首页 > 解决方案 > 关于在子类中扩展属性的问题

问题描述

我正在阅读O Reilly Python Cookbook

我对以下代码有疑问,与扩展子类中的属性有关:

class Person:
    def __init__(self, name):
        self.name = name

    # Getter function
    @property
    def name(self):
        return self._name

    # Setter function
    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._name = value

    @name.deleter
    def name(self):
        raise AttributeError("Can't delete attribute")

class SubPerson(Person):
    @property
    def name(self):
        print('Getting name')
        return super().name

    @name.setter
    def name(self, value):
        print('Setting name to', value)
        super(SubPerson, SubPerson).name.__set__(self, value)

    @name.deleter
    def name(self):
        print('Deleting name')
        super(SubPerson, SubPerson).name.__delete__(self)

我很困惑:

super(SubPerson, SubPerson).name.__set__(self, value)

使用 super(SubPerson,SubPerson) 似乎将方法作为类变量访问,为什么不能将方法作为实例变量访问?

另外,set和 @name.setter 是如何相互关联的?它们实际上不是同一件事吗?

同一行可以写成:

super(SubPerson, SubPerson).name.settr(self,value)

标签: pythonpython-3.x

解决方案


这里有两个问题。

首先是关于super(SubPerson, SubPerson)访问作为类属性的实际属性对象只是一个技巧。这里想要的是Person.name,因为一旦有了它,我们就可以使用属性的 setter 和 deleter 函数。super().name任何实例类型访问的问题是它super()不返回对象而是代理。并且代理上的属性名称只会授予对 getter 的访问权限。

后者大约@name.setter。实际上property作为初始装饰器定义name为属性对象。属性的setter属性也是一个装饰器,在其对象上设置 setter 函数并返回该对象。这意味着它只打算在类定义时使用。实际上@name.setterbloc 大致等价于类定义后的如下代码:

def foo(self, value):
    ...
SubPerson.name = SubPerson.name.setter(foo)

您要使用的方法是fset属性的属性。这意味着 setter 部分可以写成:

@name.setter
def name(self, value):
    print('Setting name to', value)
    super(SubPerson, SubPerson).name.fset(self, value)

推荐阅读