例如,在某项目中,我们实现了一些类,并希望能像静态类型语言(C,C++,Java)那样对它们的实例属性做类型检查。
p = Person()
p.name = 'Bob' #必须是str
p.age = 18 #必须是int
p.height = 1.83 #必须是float
要求:1. 可对实例属性指定类型;2. 赋予的类型不正确时抛出异常。
解决方案:
使用描述符来实现需要类型检查的属性:分别实现__get__()
、__set__()
、__delete__()
方法,在__set__()
内使用isinstance()
函数做类型检查。
- 对于描述符:
描述符就是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法充写属性的访问。这些方法有__get__()
、__set__()
,__delete__()
,如果这些方法中任何一个被定义在一个对象中,这个对象就是一个描述符。
class Descriptor:
def __init__(self, key):
self.key = key
def __set__(self, instance, value):
instance.__dict__[self.key] = value #添加属性self.key:value到属性字典__dict__中
def __get__(self, instance, cls):
return instance.__dict__[self.key] #查询属性字典__dict__中键为self.key的值value
def __delete__(self, instance):
del instance.dict__[self.key] #删除属性字典__dict__中的键值对self.key:value
class A:
x = Descriptor('x')
y = Descriptor('y')
a = A()
a.x = 5
a.y = 6
上面的描述符类中,__set__()
特殊方法的参数有:
self 描述符类的实例
instance 相当于A类的实例a
value:就是要赋予的值
__get__()
特殊方法的参数有:
self 描述符实例
instance 相当于A类的实例a
cls 相当于A类
__delete__()
特殊方法的参数有:
self 描述符实例
instance 相当于A类的实例a
- 解决方案:
class Attr:
def __init__(self, key, type_):
self.key = key
self.type_ = type_
def __set__(self, instance, value):
if not isinstance(value, self.type_):
raise TypeError('must be %s' % self.type_)
instance.__dict__[self.key] = value
def __get__(self, instance, cls):
return instance.__dict__[self.key]
def __delete__(self, instance):
del instance.dict__[self.key]
class Person:
name = Attr('name', str)
age = Attr('age', int)
height = Attr('height', float)
p = Person()
p.name = 'Bob'
p.age = 18
p.height = 17
Traceback (most recent call last): #结果
File "e:\Python practice\twenty-six\c5.py", line 27, in <module>
p.height = 17
File "e:\Python practice\twenty-six\c5.py", line 8, in __set__
raise TypeError('must be %s' % self.type_)
TypeError: must be <class 'float'>
可以看到最后的报错提示:TypeError: must be <class 'float'>
,这就达成了使用描述符对实例属性做类型检查的目的。