子类中扩展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 <module>
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)