类里的三个属性,math、chinese、english,都使用了 Property 对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢?
了解一下 Python 的描述符。
一个实现了 描述符协议 的类就是一个描述符。
什么描述符协议:实现了 __get__()、__set__()、__delete__() 其中至少一个方法的类,就是一个描述符。
__get__: 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。
__set__:将在属性分配操作中调用。不会返回任何内容。
__delete__:控制删除操作。不会返回内容。
下面的Score 类是一个描述器,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。
class Score:
def __init__(self, default=0):
self._score = default
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('Score must be integer')
if not 0 <= value <= 100:
raise ValueError('Valid value must be in [0, 100]')
self._score = value
def __get__(self, instance, owner):
return self._score
def __delete__(self):
del self._score
class Student:
math = Score(0)
chinese = Score(0)
english = Score(0)
def __init__(self, name, math, chinese, english):
self.name = name
self.math = math
self.chinese = chinese
self.english = english
def __repr__(self):
return "<Student: {}, math:{}, chinese: {}, english:{}>".format(
self.name, self.math, self.chinese, self.english
)
实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等)
image.png
参考 https://juejin.im/post/5cc4fbc0f265da0380437706
描述器应用---验证参数类型
class Typed:
def __init__(self, key, expected_type): # 构造函数接收所传入的参数和参数类型
self.key = key
self.expected_type = expected_type
def __get__(self, instance, owner):
print('get方法')
return instance.__dict__[self.key] # 从底层字典获取值
def __set__(self, instance, value):
print('set方法')
if not isinstance(value, self.expected_type): # 类型判断
raise TypeError('%s 传入的类型不是%s' % (self.key, self.expected_type)) # 格式化抛出异常
instance.__dict__[self.key] = value # 修改底层字典
def __delete__(self, instance):
print('delete方法')
instance.__dict__.pop(self.key)
class People:
name = Typed('name', str) # p1.__set__() self.__set__(),触发描述符__set__方法,设置参数类型传给构造函数
age = Typed('age', int) # p1.__set__() self.__set__()
salary = Typed('salary', float) # p1.__set__() self.__set__()
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
p1=People('alex','13',13.3)#类型有误,报错
p1 = People('alex', 13, 13.3)
print(p1.__dict__)
print(p1.name)
p1.name = 'egon'
print(p1.__dict__)
del p1.name
print(p1.__dict__)