描述符可以理解为表示对象属性的一个代理。如你的对象有代理,并且这个代理有一个“get”属性(__get__),当这个代理被调用时,你就可以访问这个对象了。当你试图使用描述符(set)给一个对象赋值或删除一个对象(delete)时,这同样适用。
严格来说,描述符可以是任何(新式)类,这种类至少实现了三个特殊方法__get__(),__set__(),__delete__()中的一个,这三个特殊方法充当描述符协议的作用。没有实现__set__()方法的类被当成方法描述符,或者准确的说是非数据描述符,那些同时实现了__get__()和__set__()的类被称作数据描述符。
__get__,__set__和__delete__的原型:
def __get__(self, obj, type = None) =>value
def __set__(self, obj, val) =>None
def __delete__(self, obj) =>None
如果实例调用了__get__,给定类X的实例x,x.foo由__getattribute__()转化为:
type(x).__dict__['foo'].__get__(x,type(x))
如果类调用了__get__,那么None将作为对象被传入:
X.__dict__['foo'].__get__(None,X)
如果super()被调用了,比如给定类Y为X的子类,然后用super(Y,obj).foo在obj.__class__.__mro__中紧接着类Y沿着继承树来查找类X,然后调用:
X.__dict__['foo'].__get__(obj,X)
然后描述符会负责返回需要的对象。
在Python中,访问一个属性的优先级顺序按照如下顺序:
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.__getattr__()方法。
class DevNull(object):
def __get__(self,obj,type = None):
pass
def __set__(self,obj,val):
pass
class C1(object):
foo = DevNull()
c1 = C1()
c1.foo = 'bar'
print 'c1.foo contains:',c1.foo
c1.foo contains: None
class DevNull2(object):
def __get__(self,obj,type = None):
print "Accessing attribute ... ignoring"
def __set__(self,obj,val):
print "Attempt to assign %r...ignoring"%(val)
class C2(object):
foo = DevNull2()
c2 = C2()
c2.foo = 'bar'
x = c2.foo
print "c2.foo contains:",x
Attempt to assign 'bar'...ignoring
Accessing attribute ... ignoring
c2.foo contains: None
class DevNull3(object):
def __init__(self,name = None):
self.name = name
def __get__(self,obj,type=None):
print "Accessing [%s]... ignoring"%(self.name)
def __set__(self,obj,val):
print "Assigning %r to [%s]...ignoring"%(val,self.name)
class C3(object):
foo = DevNull3()
c3 = C3()
c3.foo = 'bar'
x = c3.foo
print "c3.foo contains:",x
print "let us sneak it into C3 instance..."
c3.__dict__['foo'] = 'bar'
x = c3.foo
print "c3.foo contains:",x
print "c3.__dict__['foo'] contains:%r"%(c3.__dict__['foo'])
Assigning 'bar' to [None]...ignoring
Accessing [None]... ignoring
c3.foo contains: None
let us sneak it into C3 instance...
Accessing [None]... ignoring
c3.foo contains: None
c3.__dict__['foo'] contains:'bar'
class FooFoo(object):
def foo(self):
print "very important foo() method"
bar = FooFoo()
bar.foo()
bar.foo = 'It is no longer a function'
print bar.foo
del bar.foo
bar.foo()
def barBar():
print "foo() hidden by barBar()"
bar.foo = barBar
bar.foo()
del bar.foo
bar.foo()
very important foo() method
It is no longer a function
very important foo() method
foo() hidden by barBar()
very important foo() method
总结:静态方法、类方法、属性,甚至所有的函数都是描述符。描述符会根据函数的类型确定如何“封装”这个函数和函数被绑定的对象,然后返回调用对象。它的工作方式是这样的:函数本身就是一个描述符,函数的__get__()方法用来处理调用对象,并将调用对象返回给你。
REF:Core Python Programming