其实就是实现了 描述符协议 的一个类,该类的作用是避免了使用 Property 出现大量的代码无法复用。一般使用描述符是用来判断数据的合法性。
描述符协议 是指:在类里实现了 get()、set()、delete() 其中至少一个方法。
# 使用描述符判断数据的合法性
class Score:
"""
定义一个Score类描述符
当从Student的实例访问math、chinese、english这三个属性的时候,
都会经过 Score 类里的get、set、delete这三个特殊的方法
"""
def __init__(self): # 分数的默认值是0
self.__score = 0
def __get__(self, instance, owner):
return self.__score
def __set__(self, instance, value):
if not isinstance(value, int): # 使用isinstance判断输入数据是否是某一个数据类型
raise TypeError("Score must be integer")
if not 0 <= value <= 100:
raise ValueError("Valid value must be in [0, 100]")
self.__score = value
def __delete__(self, instance):
del self.__score
class Student:
"""
定义一个学生类,有四个属性name, math, chinese, english
而其中三个属性math, chinese, english都需要进行数据合法性校验
而且三个属性的合法性校验逻辑都是一样的
"""
math = Score() # 实例名称必须和init方法中实例属性名称相同
chinese = Score()
english = Score()
def __init__(self, name, math, chinese, english):
self.name = name
self.math = math
self.chinese = chinese
self.english = english
def __repr__(self): # 操作类的实例时返回指定数据
return "Student is:{}, math score is:{}, chinese score is:{}, english score is:{}".format(
self.name, self.math, self.chinese, self.english
)
if __name__ == '__main__':
st1 = Student("小明", 20, "40", 60)
print(st1)
当实例属性和数据描述符同名时,会优先访问数据描述符,而当实例属性和非数据描述符同名时,会优先访问实例属性
# 数据描述符
class DataDes:
def __init__(self, default=0):
self._score = default
def __set__(self, instance, value):
self._score = value
def __get__(self, instance, owner):
print("访问数据描述符里的 __get__")
return self._score
# 非数据描述符
class NoDataDes:
def __init__(self, default=0):
self._score = default
def __get__(self, instance, owner):
print("访问非数据描述符里的 __get__")
return self._score
class Student:
math = DataDes(0)
chinese = NoDataDes(0)
def __init__(self, name, math, chinese):
self.name = name
self.math = math
self.chinese = chinese
def __getattribute__(self, item):
print("调用 __getattribute__")
return super(Student, self).__getattribute__(item)
def __repr__(self):
return "".format(
self.name, self.math, self.chinese)
if __name__ == '__main__':
st = Student('xm', 88, 99)
print(st.math) # 实例属性和数据描述符同名,优先访问数据描述符
"""
调用 __getattribute__
访问数据描述符里的 __get__ # 先访问
88 # 后访问
"""
print(st.chinese) # 实例属性和非数据描述符同名,优先访问实例属性
"""
调用 __getattribute__
99
"""