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

来源:互联网 发布:tensorflow能用java吗 编辑:程序博客网 时间:2024/05/16 15:57

       在定义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.getterclass 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,否则会抛TypeErrorhello>>>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内部的函数。



原创粉丝点击