读书学习-Python--描述符(python3)

转自我的知乎文章(https://zhuanlan.zhihu.com/p/32487852
何为描述符?描述符就是实现了__get__、__set__和__delete__三者中任意一个的类。是用来管理数据属性的极佳方式。在学描述符之前,先来学习属性。
1.属性

在python中,对象的属性和方法都统称为属性。不管是类还是实例它们的属性都是以字典的形式存储在__dict__中。如下:

class A:
attr_a = 1
class B(A):
attr_b = 2
def init(self,c):
self.attr_c = c
"""

a = B(3)
a.attr_a,a.attr_b,a.attr_c
(1,2,3)
a.dict
{'attr_c':3}
B.dict
mappingproxy({'module': 'main', 'attr_b': 2,
'init':

而属性的存储在__dict__里也有一定的“层次感”。类的属性只会存在本类的__dict__中,实例的属性只会存在实例的__dict__中。如上,attr_c在a.__dict__中,attr_b在B.__dict__,而attr_a则在A.__dict__中。

object.getattribute(self,name)
object.setattribute(self,name,value)

一般的属性访问都是使用“.”来获取属性的,如a.attr_a。当使用“.”时,一般情况下python会调用__getattribute__来获取属性,调用__setattr__来设置属性。

python的属性读写方式有些不平等:在实例上读属性有优先序,它会先在实例上寻找,没有就去类或父类寻找,还没有就抛出AttributeError;而写属性时,若在类上时,会对实例有影响,若在实例上写属性时,只会在实例上操作,无法影响到类。

a.attr_b,a.attr_c
(2,3)
a.attr_b = 4
a.attr_b,B.attr_b
(4,2)
a.dict
{'attr_b':4,'attr_c':3}
A.attr_a = 5
a.attr_a
5
del a.attr_b
a.attr_b,a.dict
(2,{'attr_c':3})

2.描述符

那么描述符到底是用来做什么的呢?描述符是对多数属性运用相同存取方式的一种技巧。单独的描述符没有什么意义,只有描述符实例托管在另一个类(以后统称托管类)中作为类属性才有意义。

object.get(self,instance,owner)
object.set(self,instance,value)
object.delete(self,instance)

self:描述符实例

instance:托管类实例

owner:托管类

举个例子,描述符控制类属性的值不为负值。

class Des:
def init(self,s_name):
self.s_name = s_name
def get(self,instance,owner_):
return instance.dict[self.s_name]
def set(self,instance,value):
if value <= 0:
raise ValueError('value must be > 0')
instance.dict[self.s_name] = value

class One:
x = Des('x')
y = Des('y')
def init(self,x,y,z):
self.x = x
self.y = y
self.z = z
"""

a = One(1,2,3)
a.dict
{'x':1,'y':2,'z':3}
a.x
1
a.x = 0
...
ValueError: value must be > 0
a.x = 3
a.x
3
a.z = 0
a.z
0
"""

Des就是一个描述符,它控制着托管类(One)的属性。当访问属性时,python有这样一种行为:访问属性时,它会先看看是不是描述符,若是,则调用__get__或__set__来读写属性;若不是,就调用__getattribute__或__setattr__来读写属性。也就是说描述符会覆盖托管类的属性的读写方式。

描述符有__get__,__set__和__delete__三种方法,有set(和delete)的描述符是数据描述符,只有__get__的描述符是非数据描述符。一般数据描述符都有__get__和set。仅有__set__的数据描述符,除非托管实例有实例属性,否则就返回托管实例本身。

class DataDes:
def init(self):
pass
def get(self,instance,owner):
print('DataDes.get')
def set(self,instance,value):
print('DataDes.set',value)
class DataDes_noget:
def init(self):
pass
def set(self,instance,value):
print('DataDes_noget.set',value)
class non_DataDes:
def init(self):
pass
def get(self,instance,owner):
print('non_DataDes.get')
class Manage:
datades = DataDes()
data_noget = DataDes_noget()
non_data = non_DataDes()
"""

a = Manage()
a.dict
{}

有无__get__的区别

a.datades,a.data_noget
DataDes.get #调用__get__
(None, <main.DataDes_noget object at 0x00000000031D5080>, None)#返回实例
a.dict['data_noget'],a.dict['datades'] = 1,2
a.dict
{'data_noget':1,'datades':2}
a.data_noget,a.datades
2 #调用__getattribute__
DataDes.get #调用__get__

有无__set__的区别

a.datades = 1
DataDes.set 1 #调用__set__
a.datades
DataDes.get
a.non_data
non_DataDes.get
a.non_data = 2 #调用__setattribute__
a.non_data
2
a.dict
{'non_data':2}

猜你喜欢

转载自www.cnblogs.com/snowblood/p/8962514.html