#---------------------------------------------------------------------- @property deffull_name(self): """ Return the full name """ return"%s %s" % (self.first_name, self.last_name)
if __name__=="__main__": person = Person("Mike", "Driscoll") print(person.full_name) # 'Mike Driscoll' print(person.first_name) # 'Mike'
#---------------------------------------------------------------------- @Property deffull_name(self): """ Return the full name """ return"%s %s" % (self.first_name, self.last_name)
if __name__=="__main__": person = Person("Mike", "Driscoll") print(person.full_name) # 'Mike Driscoll' print(person.first_name) # 'Mike'
还记得之前我们所提到的一个定义么:Descriptors 是一种特殊的对象,这种对象实现了 __get__ ,__set__ ,__delete__ 这三个特殊方法。然后在 Python 官方文档的说明中,为了体现描述符的重要性,有这样一段话:“They are the mechanism behind properties, methods, static methods, class methods, and super(). They are used throughout Python itself to implement the new style classes introduced in version 2.2. ” 简而言之就是 先有描述符后有天,秒天秒地秒空气。恩,在新式类中,属性,方法调用,静态方法,类方法等都是基于描述符的特定使用。
OK,你可能想问,为什么描述符是这么重要呢?别急,我们接着看
使用描述符
首先请看下一段代码
1 2 3 4 5 6
classA(object): #注:在 Python 3.x 版本中,对于 new class 的使用不需要显式的指定从 object 类进行继承,如果在 Python 2.X(x>2)的版本中则需要 defa(self): pass if __name__=="__main__": a=A() a.a()
大家都注意到了我们存在着这样一个语句 a.a() ,好的,现在请大家思考下,我们在调用这个方法的时候发生了什么? OK?想出来了么?没有?好的我们继续 首先我们调用一个属性的时候,不管是成员还是方法,我们都会触发这样一个方法用于调用属性 __getattribute__() ,在我们的 __getattribute__() 方法中,如果我们尝试调用的属性实现了我们的描述符协议,那么会产生这样一个调用过程 type(a).__dict__['a'].__get__(b,type(b))。好的这里我们又要给出一个结论了:“在这样一个调用过程中,有这样一个优先级顺序,如果我们所尝试调用属性是一个 data descriptors ,那么不管这个属性是否存在我们的实例的 __dict__ 字典中,优先调用我们描述符里的 __get__ 方法,如果我们所尝试调用属性是一个 non data descriptors,那么我们优先调用我们实例里的 __dict__ 里的存在的属性,如果不存在,则依照相应原则往上查找我们类,父类中的 __dict__ 中所包含的属性,一旦属性存在,则调用 __get__ 方法,如果不存在则调用 __getattr__() 方法”。理解起来有点抽象?没事,我们马上会讲,不过在这里,我们先要解释下 data descriptors 与 non data descriptors,再来看一个例子。什么是 data descriptors 与 non data descriptors 呢?其实很简单,在描述符中同时实现了 __get__ 与 __set__ 协议的描述符是 data descriptors ,如果只实现了 __get__ 协议的则是 non data descriptors 。好了我们现在来看个例子:
deftest(self): pass if __name__=='__main__': c=Circle(4) print(c.area)
好的,让我们仔细来看看这段代码,首先类描述符 @lazyproperty 的替换过程,前面已经说了,我们不在重复。接着,在我们第一次调用 c.area 的时候,我们首先查询实例 c 的 __dict__ 中是否存在着 area 描述符,然后发现在 c 中既不存在描述符,也不存在这样一个属性,接着我们向上查询 Circle 中的 __dict__ ,然后查找到名为 area 的属性,同时这是一个 non data descriptors ,由于我们的实例字典内并不存在 area 属性,那么我们便调用类字典中的 area 的 __get__ 方法,并在 __get__ 方法中通过调用 setattr 方法为实例字典注册属性 area 。紧接着,我们在后续调用 c.area 的时候,我们能在实例字典中找到 area 属性的存在,且类字典中的 area 是一个 non data descriptors,于是我们不会触发代码里所实现的 __get__ 方法,而是直接从实例的字典中直接获取属性值。
def__get__(self, obj, objtype=None): if obj isNone: return self if self.fget isNone: raise AttributeError("unreadable attribute") return self.fget(obj)
def__set__(self, obj, value=None): if value isNone: raise TypeError("You can`t to set value as None") if self.fset isNone: raise AttributeError("can't set attribute") self.fset(obj, value)
def__delete__(self, obj): if self.fdel isNone: raise AttributeError("can't delete attribute") self.fdel(obj)