Python-cookbook之类与对象(2)

子类中扩展property

问题
在子类中,想要扩展定义在父类中的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(optional)
    @name.deleter
    def name(self):
        raise AttributeError('Can not 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)  # __set__是属性的方法
   
    @name.deleter
    def name(self):
        print('Deleting name')
        super(SubPerson, SubPerson).name__delete__(self)
  • 如果你仅仅只想扩展property的某一个方法, 那么可以这么写:
class SubPersonOne(Person):
    @Person.name.getter
    def name(self):
        print('Getting name')
        return super().name
    
    
class SubPersonTwo(Person):
    @Person.name.setter
    def name(self, value):
        print('Setting name to ', value)
        super(SubPersonTwo, SubPersonTwo).name.__set__(self, value)

讨论
在子类中扩展一个property可能会引起很多不易察觉的问题,因为一个property其实是getter、setter和deleter方法的集合,而不是单个方法。因此,当你扩展一个property的时候,你需要先确定你是否要重新定义所有的方法还是说只修改其中某 一个。
在第一个例子中,所有的property方法都被重新定义。在每一个方法中,使用了super()来调用父类的实现。在setter函数中使用super(SubPerson, SubPerson).name.__set__(self, value)的语句是没有错的。为了委托给之前定义的setter方 法,需要将控制权传递给之前定义的name属性的__set__()方法。不过,获取这个 方法的唯一途径是使用类变量而不是实例变量来访问它。这也是为什么我们要使用super(SubPerson, SubPerson).name.__set__(self, value)的原因。
如果你只想重定义其中一个方法,那只使用@property本身是不够的。比如,下面的代码就无法工作:

# 这样写会失败,应该学习SubPersonOne的写法,对单个getter重写
class SubPerson(Person):
    @property
    def name(self):
        print('Getting name')
        return super().name


s = SubPerson('locke')
Traceback (most recent call last):
  File "test.py", line 29, in 
    s = SubPerson('locke')
  File "test.py", line 3, in __init__
    self.name = name
AttributeError: can't set attribute

描述器

  • 通过构建描述器,利用描述器就可以简单地构建多个property(规则由描述器定义)
# A descriptor
class String:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, cls):
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        instance.__dict__[self.name] = value


# A class with a descriptor
class Person:
    name = String('name')

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


# Extending a descriptor with a property
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)  # __set__是属性的方法

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

你可能感兴趣的:(Python-cookbook之类与对象(2))