python学习之特殊魔法__getattr__,__getattribute__

_getattr_(self,attr)

触发时机:获取不存在的对象成员时触发
参数:1、接收当前对象的self,2、获取成员名称的字符串
返回值: 必须有值
作用:为访问不存在的属性设置值
注意:_getattribute_()无论何时都会在_getattr_()之前触发,触发了_getattribute_()有返回值就不会在触发_getattr_()了


_getattribute_(self,attr)

触发时机:使用对象成员时触发,无论成员是否存在
参数:1、接收当前对象的self,2、获取成员名称的字符串
返回值: 必须有值
作用:在具有封装操作(私有化时),为程序开部分访问权限使用

class Tree(object):
def __init__(self,name):
self.name=name
self.cate='plan'

def __getattribute__(self, obj):
print("哈哈")
return object.__getattribute__(self,obj)

aa=Tree("大树")
print(aa.name)

class Tree1(object):
def __init__(self,name):
self.name=name
self.cate='plant'

def __getattribute__(self, *args,**kwargs):
if args[0]=='大树':
print('log 大树')
return '我爱大树'
else:
return object.__getattribute__(self,*args,**kwargs)

aa=Tree1('大树')
print(aa.name)
print(aa.cate)

class Tree3(object):
def __init__(self,name):
self.name=name
self.cate='plant'

def __getattribute__(self, obj):
if obj.endswith('e'):
return object.__getattribute__(self,obj)
else:
return self.call_wind()

def __getattr__(self, item):
return self.call_wind()

def call_wind(self):
print( "树大招风")
aa=Tree3("大树")
print(aa.name)
print(aa.wind)

__getattribute__是属性访问拦截器,就是当这个类的属性被访问时,会自动调用类的__getattribute__方法。
即在上面代码中,当我调用实例对象aa的name属性时,不会直接打印,而是把name的值作为实参传进__getattribute__方法中
(参数obj是我随便定义的,可任意起名),经过一系列操作后,再把name的值返回。
Python中只要定义了继承object的类,就默认存在属性拦截器,只不过是拦截后没有进行任何操作,而是直接返回。
所以我们可以自己改写__getattribute__方法来实现相关功能,比如查看权限、打印log日志等。如下代码,简单理解即可:
class Tree3(object):
def __init__(self,name):
self.name=name
self.cate='plant'

def __getattribute__(self, obj):
if obj.endswith('e'):
return object.__getattribute__(self,obj)
else:
return self.call_wind()

def __getattr__(self, item):
return self.call_wind()

def call_wind(self):
print( "树大招风")
aa=Tree3("大树")
print(aa.name)
print(aa.wind)

————————————————
上面的解释正确吗?
先说结果,关于print(aa.name)的解释是正确的,但关于print(aa.wind)的解释不对,为什么呢?我们来分析一下,
执行aa.wind时,先调用__getattribute__方法,经过判断后,它返回的是self.call_wind(),即self.call_wind的执行结果,
但当去调用aa这个对象的call_wind属性时,前提是又要去调用__getattribute__方法,反反复复,没完没了,
形成了递归调用且没有退出机制,最终程序就挂了!
————————————————

class Human:
#成员属性
name=None
sex=None
age=None

#成员方法
#对象初始化
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age

#访问不存在成员时触发
def __getattr__(self, attr):
print('触发了__getattr__(),要访问对象的{}成员'.format(attr))
return '访问成员不存在'

#访问成员的时候触发
def __getattribute__(self, attr):
#一定不能使用self.__getattribute__(self,attr),否则会导致递归死循环,使用object的方法访问
print('触发了__getattribute__(),要访问对象的{}成员'.format(attr))
if attr in ('name','sex','age','height','test'):
return object.__getattribute__(self,attr)
else:
return '返回默认值'

def test(self):
print('调用测试方法test')

#实例化test类,分别访问对象的name(存在的成员属性)、test(存在的成员方法)、wight(不存在的成员属性),
xm=Human('小明','男',18)

#name(存在的成员属性),触发__getattribute__()方法正常返回,就不会再触发__getattr__()方法了
print(xm.name) #触发了__getattribute__(),要访问对象的name成员小明
print('\n####################################################################\n')

# test(存在的成员方法),调用和访问成员方法都会触发__getattribute__()方法
print(xm.test)
#触发了__getattribute__(),要访问对象的test成员<bound method Human.test of <__main__.Human object at 0x0000018D59C5B080>>
#触发了__getattribute__(),要访问对象的test成员调用测试方法test
xm.test()

print('\n####################################################################\n')
# wight(不存在的成员属性)
# 虽然wight不存在,但是在__getattribute__(self,attr)方法中对于访问xm.wight时设置返回‘返回默认值’
# __getattribute__(self,attr)正常返回时并没有触发__getattr__(self,attr)方法
print(xm.wight)

print('\n####################################################################\n')


# 但是当访问height成员属性时,__getattribute__(self,attr)方法并没有直接返回,
# 而是使用object.__getattribute__(self, attr)访问对象的成员,从而触发了__getattr__()

print(xm.height)

# 触发了__getattribute__(),要访问对象的height成员
# 触发了__getattr__(),要访问对象的height成员访问成员不存在

猜你喜欢

转载自www.cnblogs.com/jinpingzhao/p/12678843.html