扩展子类的属性

子类继承父类的时候,可以继承并重写父类的property,如下所示:

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

    # Deleter function
    @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)

>>> s = SubPerson('Guido') 
Setting name to Guido
>>> s.name
Getting name
'Guido'
>>> s.name = 'Larry'
Setting name to Larry
>>> s.name = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module> 
File "example.py", line 16, in name
    raise TypeError('Expected a string') 
TypeError: Expected a string
>>>

如果只想扩展property的某个方法,可以使用下面的代码:

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

或者扩展setter方法:

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

在子类中扩展属性会引入许多非常细微的问题,这些问题与以下事实有关:将属性定义为getter,setter和deleter方法的集合,而不是单个方法。 因此,在扩展属性时,您需要确定是将所有方法一起重新定义还是仅重新定义其中一种方法。
在第一个示例中,所有property方法都一起重新定义。 在每个方法中,super()用于调用先前的实现。 在setter函数中使用super(Sub Person,SubPerson).name .__ set __(self,value)没错。 若要委托给setter的先前实现,控制需要通过先前定义的name property的__set __()方法传递。 但是,访问此的唯一方法是将其作为类变量而不是实例变量进行访问。 这就是super(SubPerson,SubPerson)操作的结果。

如果只想重新定义其中的某个方法,仅仅使用@property是不够的,如下:

class SubPerson(Person):
    @propery        # Doesn't work
    def name(self):
        print('Getting name')
        return super().name

>>> s = SubPerson('Guido') 
Traceback (most recent call last):
    File "<stdin>", line 1, in <module> 
    File "example.py", line 5, in __init__
        self.name = name 
AttributeError: can't set attribute 
>>>

而是可以使用下面的方式:

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

>>> s = SubPerson('Guido') 
>>> s.name
Getting name
'Guido'
>>> s.name = 'Larry' 
>>> s.name
Getting name
'Larry'
>>> s.name = 42
Traceback (most recent call last):
    File "<stdin>", line 1, in <module> 
    File "example.py", line 16, in name
        raise TypeError('Expected a string') 
TypeError: Expected a string
>>>

在此特定解决方案中,无法用更通用的名称替换硬编码的类名称Person。 如果不知道哪个基类定义了property,则应使用以下解决方案:重新定义所有property方法,并使用super()将控制权传递给先前的实现。

posted @ 2019-12-15 15:28  Jeffrey_Yang  阅读(242)  评论(0编辑  收藏  举报