# property 属性访问器
property 用于控制访问安全类似 java 中的 get、set 方法;可以定义计算属性,例如根据半径计算直径 (diameter)。
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 from numbers import Integralclass Circle : def __init__ (self, radius ): self.radius = radius @property def diameter (self ): return self.radius * 2 @diameter.setter def diameter (self, value ): self.radius = value / 2 @property def radius (self ): return self.__dict__['radius' ] @radius.setter def radius (self, value ): if not isinstance (value, Integral) or value < 0 : raise ValueError('radius must be int and gte 0, but got {}' .format (value)) self.__dict__['radius' ] = value
# __getattr__ and __getattribute__
__getattr__
和 __getattribute__
在属性不存在时调用,如果两个同时存在, __getattribute__
会首先调用,而 __getattr__
会在 __getattribute__
抛出 AttributeError
时调用
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 class Circle : def __init__ (self, radius ): self.radius = radius def __getattribute__ (self, name ): print('__getattribute__ {}' .format (name)) def __getattr__ (self, name ): print('__getattr__ {}' .format (name)) c = Circle(1 ) d = c.radius d = c.name class Circle : def __init__ (self, radius ): self.radius = radius def __getattribute__ (self, name ): print('__getattribute__ {}' .format (name)) raise AttributeError('{} not exist' .format (name)) def __getattr__ (self, name ): print('__getattr__ {}' .format (name)) c = Circle(1 ) d = c.name
# descriptor 描述器
python 中,一个类实现了 __get__
, __set__
, __delete__
, 三个方法中的任何一个方法就是描述器,仅实现 __get__
方法就是非数据描述器,同时实现 __get__
, __set__
就是数据描述器。python 中属性的调用顺序: __getattribute__
(data descriptor -> instance property -> nondata descriptor -> class property) -> __getattr__
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from numbers import Integralclass IntField : def __get__ (self, instance, owner ): return self.value def __set__ (self, instance, value ): if not isinstance (value, Integral) or value < 0 : raise ValueError('radius must be int and gte 0, but got {}' .format (value)) self.value = value class Circle : radius = IntField() def __init__ (self, radius ): self.radius = radius c = Circle(-1 )
# 使用 descriptor 实现 lazyproperty
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 46 47 class lazyproperty : def __init__ (self, func ): self.func = func def __get__ (self, instance, cls ): if instance is None : return self else : value = self.func(instance) setattr (instance, self.func.__name__, value) return value import mathclass Circle : def __init__ (self, radius ): self.radius = radius @lazyproperty def area (self ): print('Computing area' ) return math.pi * self.radius ** 2 @lazyproperty def perimeter (self ): print('Computing perimeter' ) return 2 * math.pi * self.radius c = Circle(10 ) print(c.area) print(c.area)