例如,在某项目中,我们实现了一些类,并希望能像静态类型语言(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 = 5a.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 = 18p.height = 17Traceback (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
,这就达成了使用描述符对实例属性做类型检查的目的。