(1)什么是python属性管理?为什么要管理?在python的OOP中,属性有类的属性及实例对象的属性,有调用,赋值,删除的基本操作,属性管理就是在属性的调用,赋值,删除操作进行管理,比如,在属性赋值时,可以先判定其是不是符合要求的值,或者在调用时有其他特殊的要求。
(2)怎么样进行属性管理?属性管理主要有2种方法,一种是基于property方法或者描述符协议(discriptor protocol),构造出一个特殊的类,然后实例化后作为待管理类的类属性,该方法只能针对特定的属性,第二种是基于__getattr__或者__getattribute__的属性截取方法,在属性调用时候,截取进行操作完成既定目的。
(3)描述符协议与property方法描述符协议实际是一种定义了__set__,get,__delete__方法的类A,当该类对象被赋予为某类B的类属性后,调用类B或类B实例对象的属性,将调用类A的__get__方法,对类B或类B实例对象的属性进行赋值将调用类A的__set__方法,删除将调用类A的__delete__方法。如 下:
class Dscrip:
def __set__(self,instance,value):
print('set...')
instance._name=value
def __get__(self,instance,owner):
print('get...')
return instance._name
def __delete__(self, instance):
print('bye...')
del instance._name
class test1:
def __init__(self,name):
self._name=name
name=Dscrip()
print('test1'.center(50,'*'))
a=test1('tom')
print(a.name)
a.name='John'
print(a.name)
try:
print(test1.name)
except AttributeError as e:
print('Error',e)
del a.name
try:
print(a.name)
except:
print('Nothing...')
输出:
**********************test1***********************
get...
tom
set...
get...
John
get...
Error 'NoneType' object has no attribute '_name'
bye...
get...
Nothing...
需要说明的是描述符协议__set__(self,instance,value)中self是描述符实例对象,instance是虚拟属性所在的实例对象,value是待赋值的值,get(self,instance,owner)中,owner是instance的类,其他的同上。
property方法:
class test2:
def __init__(self,name):
self._name=name
@property
def name(self):
print('get...')
return self._name
@name.setter
def name(self,value):
print('set..')
self._name=value
@name.deleter
def name(self):
print('delete...')
del self._name
输出:
**********************test2***********************
get...
tom
set..
get...
John
<property object at 0x0000015E8CDC4278>
delete...
get...
Nothing...
实际上,property是一种特殊的描述符协议,下面是等效的property方法:
class Property:
def __init__(self,fget=None,fset=None,fdel=None):
self.fget=fget
self.fset=fset
self.fdel=fdel
def __get__(self,instance,owner):
if instance is None:
return self
if self.fget is None:
raise AttributeError('cant get attribute')
print('property get...')
return self.fget(instance)
def __set__(self,instance,value):
if instance is None:
return self
if self.fset is None:
raise AttributeError('cannt set attribute')
print('property set...')
self.fset(instance,value)
def __delete__(self,instance):
if instance is None:
return self
print('property delete...')
self.fdel(instance)
class test3:
def __init__(self,name):
self._name=name
def set_name(self,value):
self._name=value
def get_name(self):
return self._name
def del_name(self):
print('delete...')
del self._name
name=Property(get_name,set_name,del_name)
print('test3'.center(50,'*'))
a=test3('tom')
print(a.name)
a.name='John'
print(a.name)
try:
print(test3.name)
except AttributeError as e:
print('Error',e)
del a.name
try:
print(a.name)
except:
print('Nothing...')
输出:
**********************test3***********************
property get...
tom
property set...
property get...
John
property get...
Error 'NoneType' object has no attribute '_name'
property delete...
delete...
property get...
Nothing...
(4)通过__getattr__或__getattribute__操作符重载方法,该方法实际是将未知的或者所有的属性的获取进行截取,然后添加相应的操作,本质上也是虚拟属性。
简而言之,__getattr__只对未知的属性的获取进行截取,__getattributes__对所有属性获取进行截取,setattr,delattr__对所有属性的赋值,删除进行截取。
这种方法,如果__getattribute__或者__setattr,__delattr__中有对类实例对象(self)的调用,赋值,就很容易造成无限循环,因为调用,或者赋值将调用自身,防止的方法有两个:(a)用self.dict[attr]=value代替self.attr=value(b)使用object.getattribute(self,attr),object.setattribute(self,attr,value)进行更高一级的调用,赋值。
对于内建操作符的截取情况,如下图:
class GetAttr:
eggs=88
def __init__(self):
self.spam=77
def __len__(self):
print('__len__:42')
return 42
def __getattr__(self, item):
print('getattr:'+item)
if item=='__str__':
return lambda *args:'[Getattr str]'
else:
return lambda *args:None
class GetAttribute:
eggs=88
def __init__(self):
self.spam=77
def __len__(self):
print('__len__:42')
return 42
def __getattribute__(self, item):
print('getattribute:'+item)
if item=='__str__':
return lambda *args:'[Getattribute str]'
else:
return lambda *args:None
for cls in GetAttr,GetAttribute:
print('\n'+cls.__name__.ljust(50,'*'))
x=cls()
x.eggs
x.spam
x.other
len(x)
try:x[0]
except:print('fail[]')
try:x+99
except:print('fail+')
try:x()
except:print('fail()')
try:x.__len__()
except:print('fail __len__')
try:x.__call__()
except:print('fail __call__')
print(x.__str__())
print(x)
输出:
GetAttr*******************************************
getattr:other
__len__:42
fail[]
fail+
fail()
__len__:42
getattr:__call__
<__main__.GetAttr object at 0x0000021F65BF7AC8>
<__main__.GetAttr object at 0x0000021F65BF7AC8>
GetAttribute**************************************
getattribute:eggs
getattribute:spam
getattribute:other
__len__:42
fail[]
fail+
fail()
getattribute:__len__
getattribute:__call__
getattribute:__str__
[Getattribute str]
<__main__.GetAttribute object at 0x0000021F65DDE5F8>