python对象之属性访问控制--descriptor

       在定义class时,会经常使用property、classmethod和staticmethod来定义属性,使属性具有特殊的访问功能。如下所示:

class Myclass(object):                                                          
                                                                                
    def func(self, str):                                                        
        print str                                                               
                                                                                
    def get_RMB(self):                                                          
        return self.mone*6                                                      
                                                                                
    def set_dollar(self, value):                                                
        self.mone=value                                                         
                                                                                
    def del_money(self):                                                        
        del self.mone                                                           
                                                                                
    money = myproperty(get_RMB, set_dollar, del_money, 'given dollars, get RMB') 
                                                                                
    def clsfunc(cls,str):                                                       
        print 'clsfunc:',cls,str                                                
    clsfunc = myclassmethod(clsfunc)                                            
                                                                                
    def staticfunc(str):                                                        
        print 'staticfunc:',str                                                 
    staticfunc = mystaticmethod(staticfunc) 

       为什么它们能使属性的访问变得不同呢?答案在descriptor。上面的property、classmethod、staticmethod都是descriptor。

       那什么是descriptor呢?

       descriptor是一种实现__get__(), __set__(), __delete__()三个方法的class,所有实现这三个函数,并且支持特定参数的class都是descriptor。下面实现一个descriptor:

class Descriptor(object):                                                       
    def __get__(self, inst, cls):                                               
        print '__get__', inst, cls                                              
                                                                                
    def __set__(self, inst, value):                                             
        print '__set__', inst, value                                            
                                                                                
    def __delete__(self, inst):                                                    
        print '__del__', inst                                                   
                                                                                
class A(object):                                                          
    des=Descriptor()

终端直接结果:

In [2]: a = A()                                                      
                                                                                
In [3]: a.des                                                                   
__get__ <A object at 0x11eb9d0> <class 'A'>               
                                                                                
In [4]: a.des=1                                                                 
__set__ <A object at 0x11eb9d0> 1                                    
                                                                                
In [5]: del a.des                                                               
__del__ <A object at 0x11eb9d0>
                             
In [7]: A.des                                                        
__get__ None <class 'A'>                                             

In [8]: A.des=1                                                      
                                                                                
In [9]: del A.des                                                    
          
从上面的实验结果可知:

1, 通过实例化对象a读取des时,执行的是descriptor的__get__()函数,传参为(a,A)

2, 给a.des赋值时,执行的是descriptor的__set__()函数,传参为(a,value)

3, 删除a.des时,执行的是descriptor的__delete__()函数,传参为(a)

4, 通过class访问时(A.des),与a.des一样,调用__get__()函数,不过传参为(None, A)

5, 设置和删除A.des时,不进行任何操作。

       根据实现的接口不同,descriptor又可以分data descriptor和non-data descriptor。data descritor实现了__get__(), __set__(), __delete__()接口,而non-data descriptor只实现了__get__()。这两者的不同之处在于:设置和删除a.des时,data descriptor会调用__set__()和__delete__(), 而non-data descriptor表现与正常的对象属性一样,会被直接复制和删除。

有了上面的知识,我们就可以构造自己的property、classmethod、staticmethod了:

class property(object):                                                         
    def __init__(self, getter, setter=None, deller=None, doc=None):             
        self.getter = getter                                                    
        self.setter = setter                                                    
        self.deller = deller
        self.__doc__ = doc                                                         
                                                                                   
    def __get__(self, inst, cls):                                                  
        # for use class to access property                                         
        if not inst:                                                               
            return self                                                            
        return self.getter(inst)                                                   
                                                                                   
    def __set__(self, inst, value):                                                
        if not self.setter:                                                        
            raise AttributeError('xxx')                                            
        self.setter(inst, value)                                                   
                                                                                   
    def __del__(self, inst):                                                       
        if not self.deller:                                                        
            raise AttributeError('xxx')                                            
        self.deller(inst)                                                          
                                                                                   
class classmethod(object):                                                         
    def __init__(self, clsfunc):                                                   
        self.getter = clsfunc                                                      
                                                                                   
    def __get__(self, inst, cls):                                                  
        def func(*arg, **kwargs):                                                  
            return self.getter(cls, *arg, **kwargs)                                
        return func                                                                
                                                                                   
class staticmethod(object):                                                        
    def __init__(self, staticfunc):                                                
        self.getter=staticfunc                                                     
                                                                                
    def __get__(self, inst, cls):                                               
        return self.getter

class Myclass(object):                                                          
                                                                                
    def func(self, str):                                                       
        print str                                                              
                                                                               
    def get_RMB(self):                                                         
        return self.mone*6                                                      
                                                                                
    def set_dollar(self, value):                                               
        self.mone=value                                                         
                                                                                
    def del_money(self):                                                        
        del self.mone                                                           
                                                                                
    money = myproperty(get_RMB, set_dollar, del_money, 'given dollars, get RMB')
                                                                                
    def clsfunc(cls,str):                                                       
        print 'clsfunc:',cls,str                                                
    clsfunc = myclassmethod(clsfunc)                                            
                                                                                
    def staticfunc(str):                                                        
        print 'staticfunc:',str                                                 
    staticfunc = mystaticmethod(staticfunc)

下面来分析一下,一般的成员函数,为什么在我们调用时候,第一个self不需要我们传入。

>>>dir(Myclass.__dict__['func'])
['__call__',
 '__class__',
 '__closure__',
 '__code__',
 '__defaults__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__get__',     #说明Myclass的func属性是一个Non-data descriptor
 '__getattribute__',
 '__globals__',
 '__hash__',
 '__init__',
 '__module__',
 '__name__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'func_closure',
 'func_code',
 'func_defaults',
 'func_dict',
 'func_doc',
 'func_globals',
 'func_name']
>>>Myclass.__dict__['func']
<function __main__.func>
>>>Myclass.__dict__['func']('dd','hello') #此处可以看到,正常函数对参数的类型没有要求
hello

>>>Myclass.func
<unbound method Myclass.func>
>>>Myclass.func(Myclass(), 'hello')  # 此处调用的func是descriptor的返回对象,它的第一个参数必须是Myclass的instance,否则会抛TypeError
hello

>>>Myclass().func
<bound method Myclass.func of <__main__.Myclass object at 0x22a5410>>
>>>Myclass().func('hello')  #此处调用的func是descriptor的返回对象,它只需传递一个参数
hello

上实验可知:

1 当通过Myclass.__dict__['func']调用时,它是一个正常的函数(同时也是一个non-data descriptor),不是descriptor的执行结果。

2 当通过Myclass.func调用时,它是func.__get__(None, Myclass)的返回对像(unbound method)

3 通过Myclass().func调用时,它是func.__get__(Myclass(),Myclass)的返回对象(bound method)

      由此可知,class的一般成员函数,其实一个non-data descriptor,所以通过Myclass().func调用时,实际执行的该函数的__get__(),传参为(Myclass(), Myclass)。从而解释了为什么调用一般的成员函数时,无需传入self参数。

      注意所有的函数都满足这个条件,而并非只在class内部的函数。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值