《Python 描述器(descriptor).docx》由会员分享,可在线阅读,更多相关《Python 描述器(descriptor).docx(24页珍藏版)》请在第一文库网上搜索。
1、Python描述器(descriptor).docx目录1 .概述i2 .定义13 .问题24 .DescriptorProtocol75 .调用描述器96 .属性Properties107 .函数与方法FunctionsandMethods138 .静态方法与类方法159 .参考文章20环境macosx,python3.51 .概述定义描述器(descriptor),总结协议,展示描述器的调用,研究一个自定义的描述器,以及内置的python描述器,包括:函数,属性(properties),静态方法(staticmethods),以及类方法。通过给出纯python等效的以及一个样本applic
2、ation.学习有关描述器不仅仅提供通往一个更大的工具箱,它也创造了一个关于python怎么的工作的更深的理解,以及赞赏它的优雅设计。2 .定义一个descriptor是一个简单的方法管理访问属性。方式有三种:set,get,deleteo一般,一个descriptor是一个绑定行为的对象的属性,该属性可以被在描述器协议(descriptorprotocol)中的方法重写。这些方法:get_(),_set_(),delete_(),如果任何这些方法在一个对象中被定义,就说他是个描述器。默认的访问这些属性的方法是get,set,或者delete这些属性从一个对象的字典中。例如:a.x有一个查找链
3、,以开始,然后type,_dict_xL然后继续以类似的通过type基类的方式,除了元类(metaclasses),如果一个查找值是一个定义了一个描述器方法的对象,python将会重写默认的行为,并且调用描述器方法。这种情况的发生的优先链取决于哪一个描述器方法被定义了。描述器是一个强大的,通用功能的协议。他们是属性(properties),方法,静态方法,类方法,以及super。等背后的工作机理。他们被用于整个python中,来完成新实的类。3 .问题假设通过一个python写的管理系统来运营一家书店,系统中包含一个类Book,采集作者,标题,书的价格。classBook(object):de
4、f_init_(self,author,title,price):self.author=authorself.title=titleself.price=pricedef_str_(self):return0-l.format(self.author,self.title)12345678从上面的定义来看,这个设计是没什么问题,但是可以发现,书的价格可以是任意值,包括负值,这与实际情况不合作如下修改:fromweakrefimportWeakKeyDictionaryclassPrice(object):def_init_(self):self.default=0self.values=We
5、akKeyDictionary()def_get_(self,instance):returnself.values.get(instance,self.default)def_set_(self,instance,value):ifvalue100:raiseValueError(Pricemustbebetween0and100.)self.valuesinstance=valuedef_delete_(self,instance):delself.valuesinstance123456789101112131415161718注:使用弱引用weakref,使得不在使用的对象能被垃圾回收
6、。修改Book类:classBook(object):price=Price()def_init_(self,author,title,price):self.author=authorself.title=titleself.price=pricedef_str_(self):return0-l.format(self.author,self.title)12345678910b=Book(WilliamFaulkner,TheSoundandtheFury,12)b.price#12b.price=-12#Traceback(mostrecentcalllast):#Filezline1,
7、in#b.price=-12#File/line9,in_set_#raiseValueError(Pricemustbebetween0and100.)#ValueError:Pricemustbebetween0and100.b.price=101#Traceback(mostrecentcalllast):#File/line1,in#b.price=101#File/line9,in_set_#raiseValueError(Pricemustbebetween0and100.)#ValueError:Pricemustbebetween0and100.1234567891012131
8、41516171819当获取b.prce的值时,python识别出price是个描述器,并且调用Book.price._get_。当更改price的值时,如b.price=60,python再次识别price是个描述器,使用Book.price._set_替代赋值。当删除Book实例的price属性时,python自动调用Book.price.delete_以上的Price定义中使用了弱引用,如果不使用:classPrice(object):def_init_(self):self._price=0第5页共20页def_get_(self,instance):returnself._price
9、def_set_(self,instance,value):ifvalue100:raiseValueError(Pricemustbebetween0and100.)self._price=valuedef_delete_(self,instance):delself._price123456789101112131415结果:bl=Book(WilliamFaulkner,TheSoundandtheFury,12)bl.price#12b2=Book(JohnDosPassos,ManhattanTransfer,13)bl.price#131第#页共20页234567按照定义中的第二部
10、分说的。查找属性的顺序:b.diet#title*:thesoundandthefury,author:william12没有price这一属性。type(b)._dict_#mappingproxy(_init_,_doc_None,_diet_,_str_,1modulemain1weakref,price:)#ortype(b)._diet_price#123456可以知道,在给b2赋值price时,实际上修改的是price这个对象的值,同时,依据元类的原理(metaclass),Price也是类,所以bl的属性price也跟着变化。4.DescriptorProtocol第7页共20页
11、descr._get_(self,obj,type=None)-valuedescr._set_(self,obj,value)-Nonedescr._delete_(self,obj)-None如果一个对象定义类get_()以及_set_(),那么它被视为一个资料描述器(datadescriprot)o仅仅定义_get_()被称为非资料描述器(non-datadescriprot).资料描述器和非资料描述器的区别在于:相对于实例的字典的优先级。如果实例字典中有与描述器同名的属性,如果描述器是资料描述器,优先使用资料描述器,如果是非资料描述器,优先使用字典中的属性。(实例a的方法和属性重名时,
12、比如都叫fooPython会在访问a.foo的时候优先访问实例字典中的属性,因为实例函数的实现是个非资料描述器)classtest:def_init_(self):self.method=99defmethod(self):print(output.)t=test()t.method#99t.method()#Traceback(mostrecentcalllast):#File/line1,in#TypeError:intobjectisnotcallablet._diet_#method:99123456789第9页共20页10111213141516查字典的方式使人想到元类中使用type
13、进行的动态定义类的最后一个参数的定义。如果要定义一个只读的资料描述器,定义get_()以及_set_()并设置_set_()引起AttributeError的调用。5.调用描述器一个描述器可以直接使用方法的名字直接调用。d.get(obj)o或者,通过访问属性的方式是更常见的自动调用描述器的方式.例如obj.d在obj的字典中查找do如果定义了get(),然后d.get(obj)会根据优先规则列表被调用。对于对象:运行机制在object.getattribute。里面,它将b.x转换成type(b).dietget(b,type(b).工作的完成是通过优先级链,给予datadescriptor高于实例变量的优先级,或者,实例变量高于non-datadescriptor的优先级,并分配最低的优先级给getattr_()(如果存在)。整个过程的是现在Objects/object.c中的PyObject_GenericGetAttr()o对于类:运行的机制在type.getattribute(),它将B.x转换成B