[Python]面向对象编程之描述符(Descriptors)

本文介绍了Python中的描述符,这是一种特殊的属性访问机制。描述符通过实现__get__、__set__和__delete__方法参与对象属性的访问。文章详细讲解了描述符的工作原理,包括如何作为方法描述符或数据描述符使用,以及描述符调用的优先级顺序。通过实例解析,帮助读者理解这一关键特性。
摘要由CSDN通过智能技术生成

        描述符可以理解为表示对象属性的一个代理。如你的对象有代理,并且这个代理有一个“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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值