python类属性描述_Python属性、方法和类管理系列之----描述符类

什么是描述符类?

根据鸭子模型理论,只要具有__get__方法的类就是描述符类。

如果一个类中具有__get__和__set__两个方法,那么就是数据描述符,。

如果一个类中只有__get__方法,那么是非数据描述符。

__get__:当我们用类或者实例来调用该属性时,Python会返回__get__函数的结果。

__set__:当我们用实例来设置属性值时,Python会调用该函数。对类没有限制作用。

__delete__:当我们用实例试图删除该属性时,Python会调用该函数。对类没有限制作用。

非数据描述类

class Desc:

def __init__(self, value=22):

self.value= value

def __get__(self, ins, cls):

return self.value

class A:

v=Desc()

a=A()

上面的描述符类只有一个__get__属性,所以是非数据描述符。

>>> a.v #由于实例中没有v属性,所以找到了类的属性,而类的属性是一个描述符类实例,所以调用其__get__方法的结果。

22

>>> a.__dict__ #实例的__dict__空空如也。

{}

>>> A.__dict__ #类的__dict__中确实存在v属性,且是一个Desc object对象。

mappingproxy({'__module__': 'b', '__weakref__': , '__dict__': , '__doc__': None, 'v': })

>>> a.v=30 #我们通过实例设置v属性,发现成功了。

>>> a.__dict__ #我们发现实例的__dict__中存入了我们刚才设置的属性

{'v': 30}

>>> A.__dict__ #类的__dict__没有发生任何变化

mappingproxy({'__module__': 'b', '__weakref__': , '__dict__': , '__doc__': None, 'v': })

>>> a.v #如我们所料,访问到了a.__dict__中的内容。

30

>>> del a.v #我们删除实例的属性v后发现居然还是可以调用a.v,返回的是我们设置之前的值。

>>> a.v

22

>>> A.__dict__ #和前面一样,没有发生变化。

mappingproxy({'__module__': 'b', '__weakref__': , '__dict__': , '__doc__': None, 'v': })

通过上面的测试,我们发现非数据描述类有如下特点:

如果实例__dict__没有设置同名属性,那么返回描述类的__get__方法的结果。

如果实例__dict__中存在同名属性,那么返回实例__dict__中的内容。

对我们设置实例的__dict__中的行为并不做阻止。所以我说这是查看级别的描述类。

数据描述类

class Desc:

def __init__(self, value=22):

self.value= value

def __get__(self, ins, cls):

return self.value

def __set__(self, ins, value):

self.value=value

#raise AttributeError

class A:

v=Desc()

a=A()

运行结果如下:

>>> a.v

22

>>> a.v=10

>>> a.__dict__ #我们设置a.v后,发现实例的__dict__中仍然空空如也。因为此时调用的是__set__方法,值10存入到了Desc实例的value属性上了。

{}

>>> A.__dict__

mappingproxy({'__module__': 'b', '__doc__': None, '__dict__': , '__weakref__': , 'v': })

>>> a.v #此时得到的还是Desc的__get__方法返回的结果。

10

>>> del a.v #不允许我们删除

Traceback (most recent call last):

File "", line 1, in

AttributeError: __delete__

>>> A.v=30

>>> A.__dict__

mappingproxy({'__module__': 'b', '__doc__': None, '__dict__': , '__weakref__': , 'v': 30})

#我们把__set__方法的原来语句注销,添加raise AttribeError语句,再次运行

>>> a.v=30 #我们在__set__中手动添加了AttributeError异常,所以我们再也不能设置a.v的值了,因此该属性鞭策了只读属性。

Traceback (most recent call last):

File "", line 1, in

File "/home/aaa/proj/b.py", line 8, in __set__

raise AttributeError

AttributeError

>>> A.v=20 #通过类,仍然可以改变属性

>>> A.__dict__ #改变后,变成了普通属性20了,这时甚至都已经不再是描述符类了。

mappingproxy({'__doc__': None, '__dict__': , '__module__': 'b', '__weakref__': , 'v': 20})

>>> del A.v

说明如下:

当__set__方法存在后,实例设置同名属性时,完全需要看__set__的脸色。

如果描述类中__set__方法存在但是__delete__方法不存在,那么不能删除客户类中的属性。

即使在__set__方法中做了限制,这个限制只是对实例而言的,对类没有起到作用。

把属性存在描述符类中

class Desc:

def __init__(self, value):

self.value = value

def __get__(self, ins, cls):

return self.value

def __set__(self, ins, value):

self.value = value

def __delete__(self, ins):

raise AttributeError('not allowed to delete attribute name ' )

class A:

name=Desc('JS')

a=A()

执行结果如下:

>>> del a.name

>>> a=A()

>>> b=A()

>>> a.name

'JS'

>>> b.name

'JS'

>>> a.name='CC'

>>> b.name

'CC'

Traceback (most recent call last):

File "", line 1, in

File "/home/aaa/proj/b.py", line 10, in __delete__

raise AttributeError('not allowed to delete attribute name ' )

AttributeError: not allowed to delete attribute name

缺点显而易见,如果有多个实例,那么他们共享一个描述符,所以当一个实例的该属性发生改变后,其他实例的该属性也会发生变化。

改善方法:

存入一个字典,把实例的hash作为健存入,这样可以解决问题。

class Desc:

def __init__(self, value):

self.values={}

def __get__(self, ins, cls):

return self.values[hash(ins)]

def __set__(self, ins, value):

self.values[hash(ins)]=value

def __delete__(self, ins):

raise AttributeError('not allowed to delete attribute name ' )

把数据存入实例中

class Desc:

def __get__(self, ins, cls):

return ins._name

def __set__(self, ins, value):

ins._name=value

def __delete__(self, ins):

raise AttributeError('not allowed to delete attribute name ' )

class A:

name=Desc()

a=A()

执行结果如下:

>>> a=A()

>>> a.name='JS'

>>> a.name

'JS'

>>> a._name='CC'

>>> a.name

'CC'

缺点:我们设置在实例中的变量私密性不太好,可以很容易被改变。

当然,可以做一个私有性的装饰器,或者利用属性扩张来解决,这是我在后面会介绍的内容。

补充解释

__get__(self, ins, cls):其中ins为实例对象,在我们上面的例子中是a或者b,cls为a或者b的类,为A

__set__和__delete__:ins和上面的含义相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值