在定义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内部的函数。