找回密码
 立即注册

微信登录,快人一步

QQ登录

只需一步,快速开始

手机号码,快捷登录

关闭

花猫说热搜

思维 教育 情感 金融 职场 财经 管理

广告为用户自主发布,请自行甄别真假。

首页 发布中心 免费SEO 扣丁学堂Python基础教程之带你深入理解Python属性查找

扣丁学堂Python基础教程之带你深入理解Python属性查找

扣丁学堂 2020-7-8 11:07:02 IP:- 中国北京
今天扣丁学堂给大家介绍一下关于python视频教程中的属性查找,首先在Python中属性查找(attributelookup)是比较复杂的,特别是涉及到描述符descriptor的时候。首先,我们知道:python中一切都是对象,“everythingisobject”,包括类,类的实例,数字,模块任何object都是类(classortype)的实例(instance)如果一个descriptor只实现了get方法,我们称之为non-datadescriptor,如果同时实现了get__set__我们称之为datadescriptor。  
​实例属性查找:  
按照pythondoc,如果obj是某个类的实例,那么obj.name(以及等价的getattr(obj,’name’))首先调用getattribute。如果类定义了getattr方法,那么在getattribute抛出AttributeError的时候就会调用到getattr,而对于描述符(__get__)的调用,则是发生在getattribute内部的。  
obj=Clz(),那么obj.attr顺序如下:  
(1)如果“attr”是出现在Clz或其基类的dict中,且attr是datadescriptor,那么调用其get方法,否则  
(2)如果“attr”出现在obj的dict中,那么直接返回obj.dict[‘attr’],否则  
(3)如果“attr”出现在Clz或其基类的dict中  
(3.1)如果attr是non-datadescriptor,那么调用其get方法,否则  
(3.2)返回dict[‘attr’]  
(4)如果Clz有getattr方法,调用getattr方法,否则  
(5)抛出AttributeError  
下面是测试代码:  
#coding=utf-8  
classDataDescriptor(object):  
def__init__(self,init_value):  
self.value=init_value  
def__get__(self,instance,typ):  
return'DataDescriptor__get__'  
def__set__(self,instance,value):  
print('DataDescriptor__set__')  
self.value=value  
classNonDataDescriptor(object):  
def__init__(self,init_value):  
self.value=init_value  
def__get__(self,instance,typ):  
return('NonDataDescriptor__get__')  
classBase(object):  
dd_base=DataDescriptor(0)  
ndd_base=NonDataDescriptor(0)  
classDerive(Base):  
dd_derive=DataDescriptor(0)  
ndd_derive=NonDataDescriptor(0)  
same_name_attr='attrinclass'  
def__init__(self):  
self.not_des_attr='Iamnotdescriptorattr'  
self.same_name_attr='attrinobject'  
def__getattr__(self,key):  
return'__getattr__withkey%s'%key  
defchange_attr(self):  
self.__dict__['dd_base']='dd_basenowinobjectdict'  
self.__dict__['ndd_derive']='ndd_derivenowinobjectdict'  
defmain():  
b=Base()  
d=Derive()  
print'Deriveobjectdict',d.__dict__  
assertd.dd_base=="DataDescriptor__get__"  
assertd.ndd_derive=='NonDataDescriptor__get__'  
assertd.not_des_attr=='Iamnotdescriptorattr'  
assertd.no_exists_key=='__getattr__withkeyno_exists_key'  
assertd.same_name_attr=='attrinobject'  
d.change_attr()  
print'Deriveobjectdict',d.__dict__  
assertd.dd_base!='dd_basenowinobjectdict'  
assertd.ndd_derive=='ndd_derivenowinobjectdict'  
try:  
b.no_exists_key  
exceptException,e:  
assertisinstance(e,AttributeError)  
if__name__=='__main__':  
main()  
```  
[python视频教程](http://www.2xkt.com/python)  
注意第50行,change_attr给实例的__dict__里面增加了两个属性。通过上下两条print的输出如下:  
```brush:python  
Deriveobjectdict{‘same_name_attr’:‘attrinobject’,‘not_des_attr’:‘Iamnotdescriptorattr’}  
Deriveobjectdict{‘same_name_attr’:‘attrinobject’,‘ndd_derive’:‘ndd_derivenowinobjectdict‘,‘not_des_attr’:‘Iamnotdescriptorattr’,‘dd_base’:‘dd_basenowinobjectdict‘}  
调用change_attr方法之后,dd_base既出现在类的dict(作为datadescriptor),也出现在实例的dict,因为attributelookup的循序,所以优先返回的还是Clz.__dict__[‘dd_base’]。而ndd_base虽然出现在类的dict,但是因为是nondatadescriptor,所以优先返回obj.__dict__[‘dd_base’]。其他:line48,line56表明了getattr的作用。line49表明obj.__dict__优先于Clz.__dict__  
cached_property例子  
我们再来看看上一文章的这段代码  
importfunctools,time  
classcached_property(object):  
"""Apropertythatisonlycomputedonceperinstanceandthenreplaces  
itselfwithanordinaryattribute.Deletingtheattributeresetsthe  
property."""  
def__init__(self,func):  
functools.update_wrapper(self,func)  
self.func=func  
def__get__(self,obj,cls):  
ifobjisNone:returnself  
value=obj.__dict__[self.func.__name__]=self.func(obj)  
returnvalue  
classTestClz(object):  
@cached_property  
defcomplex_calc(self):  
print'verycomplex_calc'  
returnsum(range(100))  
if__name__=='__main__':  
t=TestClz()  
print'>>>firstcall'  
printt.complex_calc  
print'>>>secondcall'  
printt.complex_calc  
```  
cached_property是一个non-datadescriptor。在TestClz中,用cached_property装饰方法complex_calc,返回值是一个descriptor实例,所以在调用的时候没有使用小括号。  
第一次调用t.complex_calc之前,obj(t)的__dict__中没有”complex_calc“,根据查找顺序第三条,执行cached_property.__get__,这个函数代用缓存的complex_calc函数计算出结果,并且把结果放入obj.__dict__。那么第二次访问t.complex_calc的时候,根据查找顺序,第二条有限于第三条,所以就直接返回obj.__dict__[‘complex_calc’]。bottle的源码中还有两个descriptor,非常厉害!  
##类属性查找  
前面提到过,类的也是对象,类是元类(metaclass)的实例,所以类属性的查找顺序基本同上。区别在于第二步,由于Clz可能有基类,所以是在Clz及其基类的__dict__”查找“attr,注意这里的查找并不是直接返回clz.__dict__[‘attr’]。具体来说,这第二步分为以下两种情况:  
(2.1)如果clz.__dict__[‘attr’]是一个descriptor(不管是datadescriptor还是non-datadescriptor),都调用其__get__方法  
(2.2)否则返回clz.__dict__[‘attr’]  
这就解释了一个很有意思的问题:method与function的问题  
```brush:python  
>>>classWidget(object):  
...deffunc(self):  
...pass  
...  
>>>w=Widget()  
>>>Widget.__dict__  
dict_proxy({'__dict__':,'__module__':'__main__','__weakref__':,'__doc__':None,'func':})  
>>>w.__dict__  
{}  
>>>Widget.__dict__['func']  
>>>Widget.func  
>>>  
Widget是一个之定义了一个func函数的类,func是类的属性,这个也可以通过Widget.dict、w.dict看到。Widget.dict[‘func’]返回的是一个function,但Widget.func是一个unboundmethod,即Widget.func并不等同于Widget.dict[‘func’],按照前面的类属性的访问顺序,我们可以怀疑,func是一个descriptor,这样才不会走到第2.2这种情况。验证如下:  
classMaxValDes(object):  
def__init__(self,attr,max_val):  
self.attr=attr  
self.max_val=max_val  
def__get__(self,instance,typ):  
returninstance.__dict__[self.attr]  
def__set__(self,instance,value):  
instance.__dict__[self.attr]=min(self.max_val,value)  
print'MaxValDes__set__',self.attr,instance.__dict__[self.attr]  
classWidget(object):  
a=MaxValDes('a',10)  
def__init__(self):  
self.a=0  
#def__setattr__(self,name,value):  
#self.__dict__[name]=value  
#print'Widget__setattr__',name,self.__dict__[name]  
if__name__=='__main__':  
w0=Widget()  
w0.a=123  
```  
输出如下:  
```brush:python  
MaxValDes__set__a0  
MaxValDes__set__a10  
可以看到,即使Widget的实例也有一个‘a’属性,但是调用w.a的时候会调用类属性‘a’(一个descriptor)的set方法。如果不注释掉第18到第20行,输出如下  
Widget__setattr__a0  
Widget__setattr__a123  
可以看到,优先调用Widget的setattr方法。因此:对于属性赋值,obj=Clz(),那么obj.attr=var,按照这样的顺序:  
如果Clz定义了setattr方法,那么调用该方法,否则如果“attr”是出现在Clz或其基类的dict中,且attr是datadescriptor,那么调用其set方法,否则等价调用obj.dict[‘attr’]=var  
以上就是关于扣丁学堂带你深入理解Python属性查找的详细介绍,最后想要了解更多资讯或学习Python开发请关注扣丁学堂官网和微信公众号。扣丁学堂作为知名的IT培训机构,不仅有专业的Python培训讲师和与时俱进的课程体系,还有大量的Python全栈开发工程师视频教程供学员观看学习,想要学好Python开发技术的小伙伴就抓紧时间行动吧。扣丁学堂Python技术交流群:816572891。


花猫写作平台拥有一帮喜欢读书、喜欢写作、喜欢分享传播正能量的朋友每天与你一起学习成长,在这里你不仅仅是收获知识。转载请联系花猫说(id:hmshuyouquan)进行授权转载。
您需要登录后才可以回复 登录 | 立即注册 |
发帖

0

粉丝关注

45

主题发布

严禁黄赌毒、黑五类及违法信息,违者封号处理,已充值猫粮无法退还,请充值前确认,量大可开通VIP会员(每天5篇);请联系QQ2444830518。
花猫写作网-让你的每一个文字都有价值! 立即登录 立即注册