学习一下几个内容
- __getattr__和__setattr__方法,把未定义的属性获取和所有的属性赋值指向通用的处理器方法
- __getattribute__方法,把所有属性都指向Python3.0中类的一个泛型处理器的方法
- property内置函数,把特定属性访问定位到get和set处理函数,也叫做特性(Property)
- 描述符协议,把特定属性访问定位到具有任意get和set处理器方法的类的实例
特性协议允许我们把一个特定属性的get和set操作指向我们所提供的函数或方法,使得我们能够插入在属性访问的时候自动运行代码。
*基础知识
attribute = property(fget,fset,fdel,doc)
四个参数分别代表属性attribute的get,set,del方法和文档性信息
#-*-coding:UTF-8-*-
class Person:
def __init__(self,name):
self._name = name
def getName(self):
print('fetch...')
return self._name
def setName(self,value):
self._name = value
print('change...')
def delName(self):
print('remove...')
del self._name
name = property(getName,setName,delName,'name property docs') #属性name是通过property内置函数创建的并提供一些方法操作name的值
if __name__ == '__main__':
bob = Person('Bob Smith')
print(bob.name) #调用getName方法 获取name的值
bob.name = 'Robert Smth' #调用setName方法设置值
print(bob.name)
del bob.name
print(Person.name.__doc__)
####
fetch...
Bob Smith
change...
fetch...
Robert Smth
remove...
name property docs
[Finished in 0.3s]
像所有的类属性一样,实例和较低的子类都继承特性。
class Coder(Person):
pass
if __name__ == '__main__':
bob = Coder('Bob Smith')
print(bob.name)
###
fetch...
Bob Smith
Coder子类从Person继承了name特性
计算属性
class PropSquare:
def __init__(self, start):
self.value = start
def getX(self):
return self.value**2
def setX(self,value):
self.value = value
X = property(getX,setX)
if __name__ == '__main__':
P = PropSquare(3)
Q = PropSquare(32)
print(P.X)
P.X = 4
print(P.X)
print(Q.X)
###
9
16
1024
说明可以在自定义的函数中做一些数据方面的处理
使用装饰器来编写特性
#-*-coding:UTF-8-*-
"""
@decorator
def func(args):...
相当于
def func(args):...
func = decorator(func)
"""
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
"""name property docs"""
print('fetch.....')
return self._name
@name.setter
def name(self,value):
print('change')
self._name = value
@name.deleter
def name(self):
print('remove...')
del self._name
if __name__ == '__main__':
rose = Person('Rose Jhon')
print(rose.name)
rose.name = 'Roose Jhon'
print(rose.name)
del rose.name
###
fetch.....
Rose Jhon
change
fetch.....
Roose Jhon
remove...
装饰是编写特性的替代方法。注意set和del方法的装饰
描述符
描述符提供了拦截属性访问的一种替代方法。特性是描述符的一种,property内置函数是创建一个特定类型描述符的一种简化方法。
描述符协议允许特性我们把一个特定属性的get和set操作指向我们提供的一个单独类对象的方法:他们提供了一种方式插入在访问属性的时候自动运行的代码,并且他们允许我们拦截属性删除并且为属性提供文档。描述符作为独立的类创建,并且他们就像方法函数一样分配给类属性,也可以通过子类和实例继承
*基础知识
class Descriptor:
def __get__(self,instance,owner):...
def __set__(self,instance,value):...
def __delete__(self,instance):.....
带有这些方法的类都可以看做是描述符,并且当他们的一个实例分配给另一个类的属性的时候,它们的这些方法是特殊的----当访问属性的时候,会自动调用它们。
描述符参数:都传递了描述符类实例(self)以及描述符实例所附加的客户类的实例(instance)。__get__访问方法还额外的接收一个owner参数,指定描述符实例要附加到的类。
#-*-coding:UTF-8-*-
"""
描述符
"""
class Name:
def __get__(self,instance,owner):
print(self,instance,owner)
return instance._name
def __set__(self,instance,value):
print('change')
instance._name = value
def __delete__(self,instance):
print('remove')
del instance._name
"""
描述符生成的属性
"""
class Person:
def __init__(self,name):
self._name = name
name = Name()
"""
self是Name类的实例
instance是Person类的实例
owner是Person类
"""
if __name__ == '__main__':
bob = Person('Jim Green')
print(bob.name)
bob.name = 'Jam Green'
del bob.name
###
<__main__.Name object at 0x00610FB0> <__main__.Person object at 0x00610FD0>
Jim Green
change
remove
描述符的计算属性
计算属性
class DescSquare:
def __init__(self,start):
self.value = start
def __get__(self,instance,owner):
return self.value**2
def __set__(self,instance,value):
self.value = value
class Client1:
X = DescSquare(3) #类属性 所有实例共享的 描述符类实例化的时候传值
if __name__ == '__main__':
c1 = Client1()
print(c1.X)
print(Client1.X)
在描述符中使用状态信息
- 描述符状态用来管理内部用于描述符工作的数据
- 实例状态记录了和客户类相关的信息,以及可能有客户类创建的信息
"""
描述符状态信息
"""
class DescState:
def __init__(self,value):
self.value = value
def __get__(self,instance,owner):
print('DescState get')
return self.value*10
def __set__(self,instance,value):
print('DescState set')
self.value = value
class CalcAttrs:
X = DescState(2)
Y = 3
def __init__(self):
self.Z = 4
if __name__ == '__main__':
obj = CalcAttrs()
print(obj.X,obj.Y,obj.Z)
####
value属性仅存在描述符中,在CalcAttrs中使用value也不会冲突
对描述符存储或使用附加到客户类的实例的一个属性,不是描述符类的属性
class InsState:
def __get__(self,instance,owner):
print('InsState get')
return instance._Y*100
def __set__(self,instance,value):
print('InsState set')
instance._Y = value
class CalcAttrs1:
X = DescState(2)
Y = InsState()
def __init__(self):
self._Y = 3
self.Z = 4
if __name__ == '__main__':
obj = CalcAttrs1()
print(obj.X,obj.Y,obj.Z)
###
DescState get
InsState get
20 300 4
在运行的时候 会传递CalcAttrs1类的实例过去(instance)
在描述符类中instance._Y调用的就是obj的属性。在obj.Y的时候 会调用 InsState实例Y中的__get__方法并传递相关参数过去
__getattr__和__getattribute__
- __getattr__针对未定义的属性运行----也就是说,属性没有存储在实例上,或者没有从父类之一继承
- __getattribute__针对每个属性,当使用时,需要小心避免通过把属性访问传递给超类而导致递归循环
def __getattr__(self,name): #On undefined attribute fetch [obj.name]
def __getattribute__(self,name):#on all attribute fetch [obj.name]
def __setattr__(self,name,value):#on all attribute assignment [obj.name=value]
def __delattr__(self,name): #on all attribute deletion [del obj.name]
其中self通常是主体实例对象,name是将要访问的属性的字符串,value是要赋给该属性的对象
例子
#-*-coding:UTF-8-*-
class Catcher:
def __getattr__(self,name):
print('Get:',name)
def __setattr__(self,name,value):
print('Set:',name,value)
def test(slef):
print('test')
class Wrapper:
def __init__(self,obj):
self.wrapper = obj
def __getattr__(self,attrname):
print('Trace:',attrname)
return getattr(self.wrapper,attrname)
if __name__ == '__main__':
X = Catcher()
X.job
X.pay = 99
Y = Wrapper(X)
Y.test()
###
Get: job
Set: pay 99
Trace: test
test
####
Catcher的实例X在调用X.job时,会遭到getattr方法的拦截(拦截没有定义的属性)
注意使用__setattr__和__getattribute__要避免循环
#-*-coding:UTF-8-*-
class Person:
def __init__(self,name):
self._name = name
# def __getattr__(self,attr):
# if attr == 'name':
# print('fetch....')
# return self._name
# else:
# raise AttributeError(attr)
def __getattribute__(self,attr):
if attr == 'name':
print('fetch')
attr = '_name'
return object.__getattribute__(self,attr)
def __setattr__(self,attr,value):
if attr=='name':
print('change...')
attr = '_name'
print(attr)
self.__dict__[attr] = value #避免循环 如果使用self._name = value会产生递归循环
def __delattr__(self,attr):
if attr == 'name':
print('remove...')
attr = '_name'
del self.__dict__[attr]
if __name__ == '__main__':
bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Robert Smith'
print(bob.name)
del bob.name
print('-'*20)
sue = Person('Sue Jones')
print(sue.name)
- 构造函数中的self._name = name会触发__setattr__
__getattr__和__getattribute__比较
- __getattr__拦截未定义的属性,已经定义的属性不会拦截
- __getattribute__拦截所有的属性获取,并且需要将没有管理的属性访问指向超类获取器以避免循环