python通过管理实现,实现实例在属性访问时自动运行对应代码。
获取属性值以及存储属性值对其验证和修改的时候,代码可以动态的计算属性值。
(1)__getattr__和__setattr__方法,把未定义的属性获取和所有的属性赋值指向通用的处理器方法。
(2)__getattribute__方法,把所有属性获取都指向python2.x的新式类和python3.x的所有类的一个泛型处理器方法。
(3)property内置函数,把特定属性访问定位到get和set处理器函数,也叫做特性(Property)。
(4)描述符协议,把特定属性访问定位到具有任意get和set处理器方法的类的实例。
NO | 管理属性方法 | 描述 |
---|---|---|
1 | 特性 | 1、 用property()内置函数;定义getX、setX、delX方法; 2、 用@property装饰器;定义setter、deleter方法; 3、 客户类实例属性用双下划线开头,和特性类属性区分; 4、 属性操作未定义对应方法,则报错; |
2 | 描述符 | 1、 有__get__()、__set__()、__delete__()方法的类,为描述符; 2、 描述符实例作为客户类属性; 3、 若用描述符属性,客户类实例属性不能用双下划线开头; 4、 禁止赋值需显式声明描述符的__set__()方法为只读; |
3 | __getattr__ | 1、 实例访问未定义属性name,被拦截指向已定义属性 _name; 2、 对未定义属性进行判断匹配,实现对应逻辑; |
4 | __getattribute__ | 1、 实例访问全部属性被拦截; 2、 对属性名进行判断匹配,实现对应逻辑; 3、 通过object.__getattribute__(self,attr)避免循环; |
5 | __setattr__ | 1、 拦截实例属性赋值操作; 2、 通过self.__dict__[attr]=value、object.__setattr__(self,attr,value) |
示例
>>> class PrintBusiCard:
def __str__(self,obj):
t=(obj.cardid,obj.name,obj.age,obj.addr,obj.remain2retire)
m=map(str,t)
return ','.join(tuple(m))
>>> class BusiCardProp:
'''
特性未定义__set__()时attr=value赋值报错
客户类实例属性用双下划线开头
特性的set、get直接操作__x
'''
cardidlen=10
retireage=65
def __init__(self,cardid,name,age,addr):
self.__cardid=cardid
self.__name=name
self.__age=age
self.addr=addr
def getName(self):
return self.__name
def setName(self,value):
self.__name=value
name=property(getName,setName)
def getAge(self):
return self.__age
def setAge(self,value):
if value<=0 or value>160:
errmsg='年龄需大于0,小于等于160'
raise ValueError(errmsg)
else:
self.__age=value
age=property(getAge,setAge)
@property
def cardid(self):
return self.__cardid[:-5]+'*****'
@cardid.setter
def cardid(self,value):
if len(value)!=self.cardidlen:
errmsg='卡号长度需等于10'
raise ValueError(errmsg)
else:
self.__cardid=value
@property
def remain2retire(self):
return self.retireage-self.__age
# 特性未定义__set__()时attr=value赋值报错
def __str__(self):
return PrintBusiCard().__str__(self)
>>> class BusiCardDesc:
'''
描述符实例作为属性,访问时自动调用描述符实例的方法
若用描述符属性,客户类实例属性不能用双下划线开头,
因为外部类无法访问__x属性。
若未定义__set__,attr=value赋值,直接使用默认赋值成功,
所以只读的需显式申明
'''
cardidlen=10
retireage=65
def __init__(self,cardid,name,age,addr):
self._cardid=cardid
self._name=name
self._age=age
self.addr=addr
class CardId:
def __get__(self,instance,owner):
return instance._cardid[:-5]+'*****'
def __set__(self,instance,value):
if len(str(value))!=instance.cardidlen:
errmsg='卡号长度需等于10'
raise ValueError(errmsg)
else:
instance._cardid=value
class Name:
def __get__(self,instance,owner):
return instance._name
def __set__(self,instance,value):
instance._name=value
class Age:
def __get__(sel,instance,owner):
return instance._age
def __set__(self,instance,value):
if value<=0 or value>160:
errmsg='年龄需大于0,小于等于160'
raise ValueError(errmsg)
else:
instance._age=value
class Remain2Retire:
def __get__(self,instance,owner):
return instance.retireage-instance._age
# 描述符必须显式定义__set__()为只读
# 若未定义,attr=value赋值,直接使用默认赋值成功
def __set__(self,instance,value):
raise AttributeError('禁止修改')
cardid=CardId()
name=Name()
age=Age()
remain2retire=Remain2Retire()
def __str__(self):
return PrintBusiCard().__str__(self)
>>> class BusiCardGetAtrr:
'''
实例访问未定义属性name,被拦截指向已定义属性 _name
'''
cardidlen=10
retireage=65
def __init__(self,cardid,name,age,addr):
self._cardid=cardid
self._name=name
self._age=age
self.addr=addr
def __getattr__(self,name):
if name == 'cardid':
return self._cardid[:-5]+'*****'
elif name == 'name':
return self._name
elif name == 'age':
return self._age
elif name == 'remain2retire':
return self.retireage-self._age
else:
raise AttributeError(name)
def __setattr__(self,name,value):
if name == 'cardid':
if len(str(value))!=self.cardidlen:
errmsg='卡号长度需等于10'
raise ValueError(errmsg)
name='_cardid'
elif name == 'name':
name='_name'
elif name == 'age':
if value<=0 or value>160:
errmsg='年龄需大于0,小于等于160'
raise ValueError(errmsg)
name='_age'
elif name == 'remain2retire':
raise AttributeError('禁止修改')
self.__dict__[name]=value
def __str__(self):
return PrintBusiCard().__str__(self)
>>> class BusiCardGetAtrriBute:
'''
实例访问全部属性被拦截
'''
cardidlen=10
retireage=65
def __init__(self,cardid,name,age,addr):
self.cardid=cardid
self.name=name
self.age=age
self.addr=addr
def __getattribute__(self,name):
objget=object.__getattribute__
if name == 'cardid':
return objget(self,'cardid')[:-5]+'*****'
elif name == 'remain2retire':
return objget(self,'retireage')-objget(self,'age')
else:
return objget(self,name)
def __setattr__(self,name,value):
if name == 'cardid':
if len(str(value))!=self.cardidlen:
errmsg='卡号长度需等于10'
raise ValueError(errmsg)
elif name == 'age':
if value<=0 or value>160:
errmsg='年龄需大于0,小于等于160'
raise ValueError(errmsg)
elif name == 'remain2retire':
raise AttributeError('禁止修改')
self.__dict__[name]=value
def __str__(self):
return PrintBusiCard().__str__(self)
>>> def testBC(BCDClass):
print('当前BCDClass测试类为:'+BCDClass.__name__)
bc1=BCDClass('1234567890','张三',39,'深圳')
print(bc1)
bc1.name='梯阅线条'
bc1.age=33
bc1.cardid='0123456789'
print(bc1)
bc2=BCDClass('0755123456','李四',21,'广州')
print(bc2)
try:
bc2.age=211
except ValueError as ve:
print('年龄设置错误:'+str(ve))
try:
bc2.cardid='211'
except ValueError as ve:
print('卡号设置错误:'+str(ve))
try:
bc2.remain2retire=211
except Exception as e:
print('距离退休设置错误:'+str(e))
>>> for c in (BusiCardProp,BusiCardDesc,BusiCardGetAtrr,BusiCardGetAtrriBute):
print('\n'+c.__name__.center(50,'='))
testBC(c)
===================BusiCardProp===================
当前BCDClass测试类为:BusiCardProp
12345*****,张三,39,深圳,26
01234*****,梯阅线条,33,深圳,32
07551*****,李四,21,广州,44
年龄设置错误:年龄需大于0,小于等于160
卡号设置错误:卡号长度需等于10
距离退休设置错误:can't set attribute
===================BusiCardDesc===================
当前BCDClass测试类为:BusiCardDesc
12345*****,张三,39,深圳,26
01234*****,梯阅线条,33,深圳,32
07551*****,李四,21,广州,44
年龄设置错误:年龄需大于0,小于等于160
卡号设置错误:卡号长度需等于10
距离退休设置错误:禁止修改
=================BusiCardGetAtrr==================
当前BCDClass测试类为:BusiCardGetAtrr
12345*****,张三,39,深圳,26
01234*****,梯阅线条,33,深圳,32
07551*****,李四,21,广州,44
年龄设置错误:年龄需大于0,小于等于160
卡号设置错误:卡号长度需等于10
距离退休设置错误:禁止修改
===============BusiCardGetAtrriBute===============
当前BCDClass测试类为:BusiCardGetAtrriBute
12345*****,张三,39,深圳,26
01234*****,梯阅线条,33,深圳,32
07551*****,李四,21,广州,44
年龄设置错误:年龄需大于0,小于等于160
卡号设置错误:卡号长度需等于10
距离退休设置错误:禁止修改