描述符简介
而__getattr__ ,__getattribute__ 属于运算符重载,因此他们可以作用于代理类,最后说明了一些__getattribute__的坑.
关于特性需要注意不要把特性对象的名字与实例属性或者类属性的名字一样,
如果把特性对象与类属性名字一样 , 则按照覆盖规则来定义,后面的覆盖前面的;
如果与实例属性名字一样,则有可能会出现无限循环(递归) .
python中提供了大量拦截的操作:
1.运算符重载 (与c++中的operator x()相似)
2.__getattr__ ,__setattr__ , __getattribute__ , 拦截属性
3.描述符/特性 对某一特定的类属性拦截
4.装饰器(函数/类装饰器) , 重定向各种函数,类 . 可以混合使用,非常灵活
#管理属性比较
#注意:
#1.__getattr__ 拦截未定义的属性
#2.__getattribute__ 拦截所有属性,其优先级高于__getattr__
#3.__setattr__ 拦截所有属性赋值
#4.使用object.__getattribute__,object.__setattr__ 避免无限循环
#第一种:property
#property 使用的时候注意命名冲突.
#property 是一种描述符
class Powers:
def __init__(self,sq,cu):
self._sq = sq #self.square = sq 将出错,会无限循环的调用property已设置的setsq
self._cu = cu
def getSq(self):
print('getsq')
return self._sq ** 2
def setSq(self,value):
print('setSq')
self._sq = value
def getCu(self):
return self._cu ** 3
#2个property对象(描述符对象)
cube = property(getCu)
square = property(getSq,setSq)
a = Powers(4,5)
print(a.square) #getsq , 16 . 实际调用property对象的__get__
a.square = 10 #setsq . 实际调用property对象的__set__
#第二种 , 描述符
class DescSq(object):
def __get__(self, instance, owner):
print('__get__')
return instance._sq ** 2 #假设instance有属性_sq
def __set__(self, instance, value):
print('__set__')
instance._sq = value
class DescCu:
def __get__(self, instance, owner):
return instance._cu ** 3
class Power:
square = DescSq()
cube = DescCu()
def __init__(self,sq,cu):
self._sq = sq #在描述符中如果使用self.square = sq 则可以,会直接调用描述符对象中的__set__
self._cu = cu
a = Power(3,4)
a.square = 5 #__set__
print(a.square) #__get__
#第三种,__getattr__ , __setattr__
#__getattr__ 拦截未定义属性, __setattr__ 拦截所有赋值
#使用object.__setattr__ 来防止无限循环
class Powers:
def __init__(self,sq,cb):
self._sq = sq
self._cb = cb
def __getattr__(self,value):
print('__getattr__ ,', value)
if 'square' == value:
return self._sq ** 2
elif 'cube' == value:
return self._cb ** 3
else:
raise TypeError('未知属性:'+str(value))
def __setattr__(self, key, value):
print('__setattr__')
if 'square' == key:
key = '_sq'
elif 'cube' == key:
key = '_cb'
object.__setattr__(self,key,value)
a = Powers(5,6)
a.square = 11 #__setattr__
print(a.square) #__getattr__
print(' *** __getattribute__ ***')
#第4种,使用__getattribute__
#注意避免无限循环 使用object.__getattribute__ , object.__setattr__
class Powers:
def __init__(self,sq = 5,cb=6):
self._sq = sq
self._cb = cb
def __getattribute__(self, item):
print('__getattribute__')
if 'square' == item:
item = '_sq'
return object.__getattribute__(self,item) ** 2
elif 'cube' == item:
item = '_cb'
return object.__getattribute__(self,item) ** 3
return object.__getattribute__(self,item)
def __setattr__(self, key, value):
print('__setattr__')
if 'square' == key:
key = '_sq'
elif 'cube' == key:
key = '_cb'
object.__setattr__(self,key,value)
b= Powers()
b.square = 10
print(b.square)
__getattr__ 与 __getattribute :
#__getattr__ 与 __getattribute 作用于委托
#以下例子说明了__getattr__ 只会对未定义的属性调用
class Person:
def __init__(self,name, job = None,pay = 0):
self.name = name
self.job = job
self.pay = pay
def lastName(self):
return self.name.split()[-1]
def giveRaise(self,percent):
self.pay = int(self.pay * (1 +percent))
def __str__(self):
return '[Person:%s,%s]' %(self.name, self.pay)
class Manager:
def __init__(self,name,pay):
self.person = Person(name,'mgr',pay)
def giveRaise(self,percent,bonus = 0.1):
self.person.giveRaise(percent+bonus)
def __getattr__(self, item):
print('__getattr__ item:',item)
return getattr(self.person,item)
def __str__(self):
print('__str__')
return str(self.person)
t = Manager('law',30)
print(t.lastName()) #Manager.__getattr__ => Person.lastName
t.giveRaise(10) #Manager.giveRaise => Person.giveRaise
print(t) #Manager.__str__ => Person.__str__
#echo
#__getattr__ item: lastName
#law
#__str__
#[Person:law,333]
#__getattribute__ 进行修改上面的Manager 进行委托
#__getattribute__ 需要小心使用,否则将无限循环
#注意,下面例子中若在__getattribute__中没有对person进行处理,将会产生无限循环
class Manager1:
def __init__(self,name,pay):
self.person = Person(name,'mgr',pay)
def giveRaise(self, percent, bonus=0.1): #此函数将永久不会执行!! __getattribute__进行拦截
print('Manager1.giveRaise')
self.person.giveRaise(percent + bonus)
def __getattribute__(self, item): #拦截了所有属性
print('__getattribute__ , item:' , item)
person = object.__getattribute__(self,'person') #防止无限循环
return getattr(person,item) #使用getattr对person调用
def __str__(self):
person = object.__getattribute__(self,'person') #此处不能使用self.person,除非修改__getattribute__的代码:return object.__getattribute__(self,'person')
return str(person)
t = Manager1('manager1',30)
t.giveRaise(10) #此处调用的是Person.giveRaise ,自身的giveRaise 并没有调用
print(t)
#echo:
#__getattribute__ , item: giveRaise
#[Person:manager1,330]