现在试想一下我们有一个这样的场景。
class Person:
def __init__(self, name, gender):
self.name = name
self.gender = gender
我们希望在这里加入类型检查,name和gender都必须为字符串。
熟悉python的同学当然想到了property。那我们的代码就变成了这样子。
class Person:
def __init__(self, name, gender):
self.name = name
self.gender = gender
@property
def name(self):
return self._name
@name.setter
def name(self, name):
if isinstance(name, str):
self._name = name
else:
raise AttributeError('name must be a string !')
@property
def gender(self):
return self._gender
@gender.setter
def gender(self, gender):
if isinstance(gender, int):
self._gender = gender
else:
raise AttributeError('gender must be a string !')
有没有发现一个很严重的问题,我们写了重复的逻辑,这样做是个十分不好的设计。我们希望自己写的类似于类型检查的逻辑可以复用。
下面就该我们描述符出场了,我们的代码变为了下面这样子。
class StringField:
def __get__(self, instance, owner):
return self.data
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError('need string !')
self.data = value
def __delete__(self):
...
class Person:
name = StringField()
gender = StringField()
if __name__ == '__main__':
p = Person()
p.name = 'Tom'
print(p.name)
p.name = 123
我们看一下输出结果。
Tom
Traceback (most recent call last): File "/Users/shizhentao/PythonProjects/flask-demo/test.py", line 30, in
p.name = 123 File "/Users/shizhentao/PythonProjects/flask-demo/test.py", line 8, in __set__
raise ValueError('need string !')
ValueError: need string !
跟上面的代码实现的效果一样,但是我们实现的十分精简。