浅谈Python中的描述符(Descriptor)
简单点
1 2 3 4 5 6 7 8
| class Descriptor: def __get__(self, instance, owner): print('__get__ called. ') class foo: t = Descriptor() if __name__ == '__main__': c = foo() c.t
|
详细点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import types class Descriptor: def __call__(self, *args, **kwargs): print('Descriptor called') def __get__(self, instance, owner): if instance is not None: print('__get__ called. ' + 'instance is ' + str(instance) + '; owner is ' + str(owner)) return types.MethodType(self, instance) else: print('__get__ called. ' + 'instance is ' + str(instance) + '; owner is ' + str(owner)) return self def __set__(self, instance, value): print('set ' + str(instance) + ';value: ' + str(value)) class foo: t = Descriptor() class bar: def __init__(self): self.t=Descriptor() if __name__ == '__main__': c = foo() print('test 1') foo.t print('test 2') c.t print('test 3') c.t() print('test 4') c.t = 1 print('test 5') d=bar() d.t ''' test 1 __get__ called. instance is None; owner is <class '__main__.foo'> test 2 __get__ called. instance is <__main__.foo object at 0x00000273839F4320>; owner is <class '__main__.foo'> test 3 __get__ called. instance is <__main__.foo object at 0x00000273839F4320>; owner is <class '__main__.foo'> Descriptor called test 4 set <__main__.foo object at 0x00000273839F4320>;value: 1 test 5 '''
|
有什么用
如果想创建一个新形式的实例属性,可以以描述符类的形式定义其功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Integer: def __init__(self, name): self.name = name def __get__(self, instance, cls): if instance is None: return self else: return instance.__dict__[self.name] def __set__(self, instance, value): if not isinstance(value, int): raise TypeError('Expected an int') instance.__dict__[self.name] = value def __delete__(self, instance): del instance.__dict__[self.name] class Point: x = Integer('x') y = Integer('y') def __init__(self, x, y): self.x = x self.y = y if __name__ == '__main__': p = Point(2, 3) print(p.x) p.y = 5 try: p.x = 2.3 except TypeError as e: print(e)
|
如果仅仅是像访问某个特定类中的一个属性,并对此做定制化处理,直接用property更加简单。
在需要大量重用代码的情况下,描述符更加有用。