Pyhon中set,get,方法深入了解

来源:互联网 发布:19级研究所升级数据 编辑:程序博客网 时间:2024/05/01 04:14
</pre><pre name="code" class="python">首先要弄明白dir()和__dict__的区别:
class T(object):      name = 'name'      def hello(self):          print 'hello'  t = T() 
我们定义了一个类T和一个实例:

 

>>> dir(t)  ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',   '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',   '__repr__', '__setattr__', '__str__', '__weakref__', 'hello', 'name'] 
<pre name="code" class="python">
>>>dir(T)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'hello', 'name']
</pre><pre name="code" class="python">>>>t.__dict__
{}
</pre><pre name="code" class="python">>>>T.__dict__

dict_proxy({'__module__': '__main__', 'name': 'name', 'hello': <function hello at 0x7f5412fca5f0>, '__dict__': <attribute '__dict__' of 'T' objects>, '__weakref__': <attribute '__weakref__' of 'T' objects>, '__doc__': None})

可以看出,dir()里面存储的属性和 .__dict__里面存储的属性有着很大的区别, __dict__里面存储着自定义的属性,dir()除了自定义的属性还有python自动生成的,而属性查找get和属性设置set 都是基于__dict__来进行的,也就是说我们现在只用关心自定义的属性。


数据描述符,下面这个类产生的实例即是一个数据描述符(区分非数据描述符,数据描述符一定要带有__get__和__set__方法;如果只有__get__,没有__set__就是非数据描述符)

class Descriptor(object):      def __get__(self, obj, type=None):              return 'get', self, obj, type      def __set__(self, obj, val):          print 'set', self, obj, val  
class T(object):
    d=Descriptor()
t=T()

__get__方法:

__get__方法目的就是为了赋值,首先看下参数的意义,第一个self,是当前Descriptor 的实例,第二个obj,是拥有Descriptor 这个属性的对象,第三个type就是obj所属的类,举个例子说明:

声明了t.d    则obj 就是t,  type 记为T,所以t.d实际上是返回d.__get__(t,T) 的结果

如果T.d  则返回的是d.__get__(None,T)  ....这是当类直接调用__get__方法的结果,

首先看下

>>>T.__dict
dict_proxy({'__dict__': <attribute '__dict__' of 'T' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'T' objects>, 'd': <__main__.Descriptor object at 0x7f5412fc8a10>, '__doc__': None})
>>>t.__dict__
{}
</pre>可以看到t中自定义属性还是没有,T.__dict__中的d是一个数据描述符 data descriptor(可以将数据描述符看成类T中特殊的方法,因为该方法本来就是一个类,而且包含着__get__和__set__方法)<p></p><p>那么我要查找t.d那怎么办呢?</p><p>首先,在t.__class__.__dict__中查找,就是T.__dict__中查找,发现了d,再判断d是否是data descriptor,是的话,就返回get方法的结果,所以返回的应该是</p><p></p><pre name="code" class="python">>>>t.d
('get', <__main__.Descriptor object at 0x7f5412fc8a10>, <__main__.T object at 0x7f5412fc8a50>, <class '__main__.T'>)
>>>T.d
('get', <__main__.Descriptor object at 0x7f5412fc8a10>, None, <class '__main__.T'>)


现在以一般的概念来说属性查找策略,比如要查找obj.attr  (obj是实例,要查找attr属性),我写的是简化版

1. 首先在obj.__class__.__dict__中查找attr,也就是在obj的父类中查找,如果attr存在,并且是data descriptor,  则返回__get__方法的结果,如果没有,则在 祖先中查找,一步一步找,如果都没有,就进行第二步(这时候,只有两种情况,要不attr在父类中存在,且不是data descriptor,要不就是没在父类或者祖先中)

2.在obj.__dict__中查找,如果没找到,第三步

3.回到父类中,在obj.__class.__dict__中查找,这时候要求没那么高了,不要求是数据描述符,只要有就找到,如果父类没有,去祖先那里找

4.如果还没找到,sorry,无能为力了,如果没有异常处理,就会直接出错卡出来。


这个我们来验证一下以往的一个简单例子

class A(object):     A.name='hello'a=A()

当给a.name='nihao'后,现在要查找a.name. 首先在父类A中搜索,确实有a,但是a 并不是数据描述符,所以回到a.__dict___中搜索,所以会所搜到   ' nihao'   .

如果del a.name后,那么首先在父类A中搜索,确实有a,但是a 并不是数据描述符,所以回到a.__dict___中搜索,然而也没有搜索到 name, 因为我们已经删除了,所以再回到父类中搜索,这时候要求没那么高,搜索到了name ,值是 'hello',  所以会输出 ‘hello'

__set__方法

类似于__get__方法,我们首先给出赋值策略,再来详细例子介绍: 给obj.attr=value.   

1.查找obj.__class__.__dict__,如果attr存在并且是个data descriptor , 则调用attr的__set__方法,如果不存在,现在祖先中搜索,如果实在没有,则到第二步

2.    这时候说明attr不存在或者不是数据描述符,  则直接obj.__dict__['attr']=value

考虑下调用t.d='scut' 和调用 T.d= 'SCUT'  有什么区别呢?

>>>t,d='scut'
set <__main__.Descriptor object at 0x7f5412fc8a10> <__main__.T object at 0x7f5412fc8a50> scut
很明显,调用了描述符中的__set__方法,因为在父类中查找d的时候,就已经发现 d  是一个数据描述符,则直接调用数据描述符的__set__方法
>>>T.d='SCUT'
>>>T.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'T' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'T' objects>, 'd': 'hello', '__doc__': None})

可以看出,这时候没有调用__set__方法,为什么呢?根据赋值策略,T的父类是object不存在数据描述符d,则到第二步,直接赋值,这就是为什么T.d 可以赋值,而 t.d 却不可以赋值的原因,如果d不是一个数据描述符的话,d 也可以赋值

验证一下我们之前的简单例子

class A(object):     A.name='hello'a=A()

当给a.name=‘nihao’ 进行赋值的时候,name不是一个数据描述符,则直接进行第二步进行操作,直接在实例a 自定义属性中增加一个name,当删除这个name后,a.name='hello'.   因为在它的自定义属性中的字典中找不到name了. 

下一节学习property内建函数,property与__set__和__get__方法是联系在一起的。




0 0
原创粉丝点击