属性查找顺序
:
正常情况下,属性查找都是以一定的规则从__dict__中查找的。 如果只有类属性x,没有实例属性x,当访问x的时候,会是如何呢?我们来看下面例子:
class Test(object):
x = 8
def __init__(self,c):
pass
t= Test(6)
t.x
print(t.x)
# 输出结果:
>>>>>8
以上例子中,表明当没有实例属性时即查找不到实例属性,会查找类属性。
属性访问的默认行为是从一个对象的字典中获取、设置或删除属性。例如,a.x 的查找顺序会从 a.__dict__['x']
开始,然后是 type(a).__dict__['x']
,接下来依次查找 type(a) 的上级基类,不包括元类。
注意:当类中定义了__slots__属性时,对象就不会有__dict__属性了,这时访问属性时,是通过类似描述符的方式查找属性的。
一般情况下,python的属性访问机制是:实例属性,类属性,父类属性,object属性;也就是先查找实例对象的__dict__属性,没有的话再查找类__dict__属性,再没有的话查找父类的__dict__属性,最后是基类object属性。当定义了数据描述符时时,会覆盖实例对象的__dict__属性;非数据描述符访问属性时,先查找对象的__dict__属性,没有的话再调用描述符的__get__方法。
属性访问顺序:
- 调用__getattribute__
- 调用数据描述符(同时定义
__get__(),__set__()
的描述符) - 调用实例对象属性
- 调用类的属性
- 调用非数据描述符(仅定义
__get__()
的描述符) - 调用父类的属性
- 调用__getattr__(属性不存在时调用)
__getattribute__
:
触发时机:访问对象的成员属性的时候自动触发,无论该成员属性是否存在
作用:可以在用户访问成员属性的时候进行数据处理等操作
参数:self接收当前对象,第二个参数接收访问的成员属性名称字符串 (object.__getattribute__(self, name)
)
返回值:可有可无,没有的话就返回None
注意事项:在该魔术方法中,禁止使用当前对象.成员(self.XX)
的方式(例如:self.name)访问成员,否则会发生递归次数过大的错误RecursionError: maximum recursion depth exceeded while calling a Python object
class Human:
def __init__(self):
self.name = 'Python'
self.sex = 'male'
self.age = 18
def __getattribute__(self, item):
print('getattribute call by %s' % self)
# return self.name 如果返回的是self.name,则会报错
return super().__getattribute__(item)
def eat(self):
print('eat method is running')
human = Human()
print(human.name)
print(human.age)
# 输出结果:
>>>>>getattribute call by <__main__.Human object at 0x03EAD088>
>>>>>Python
>>>>>getattribute call by <__main__.Human object at 0x03EAD088>
>>>>>18
__getattr__
:
触发时机:访问不存在的对象成员属性的时候自动触发
作用:防止访问不存在的对象成员的时候报错;为不存在的成员定义值
参数:self接收当前对象,第二个参数接收访问的成员属性名称字符串(object.__getattr__(self, name)
)
返回值:可有可无,没有的话就返回None
注意事项:__getattribute__和__getattr__可以同时存在,如果同时存在,那么在访问不存在的值时,__getattribute__和__getattr__都会触发
该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。
class Human:
# 成员属性
def __init__(self):
self.name = 'Python'
self.sex = 'male'
self.age = 18
def __getattr__(self, item):
if item == 'score':
return 80
else:
return 'No Attribution'
def eat(self):
print('eat method is running')
human = Human()
print(human.score)
print(human.school)
# 输出结果:
>>>>>80
>>>>>No Attribution
__setattr__
:
触发时机:添加或者修改对象成员的时候自动触发
作用:可以限制或者管理对象成员的添加或修改操作
参数:self接收当前对象,第二个参数接收设置的成员名称字符串,第三个参数接收设置的值(object.__setattr__(self, name, value)
)
返回值:无
注意事项:在该魔术方法中,禁止使用当前对象.成员=值
的方式(例如:self.name = ‘Mike’)设置成员,否则会发生递归次数过大的错误RecursionError: maximum recursion depth exceeded while calling a Python object
class Human:
# 成员属性
def __init__(self):
self.name = 'Python'
self.sex = 'male'
self.age = 18
def __setattr__(self, item,value):
if item == "score": # 限制添加score
pass
else:
return super(Human,self).__setattr__(item,value) # 此处不可以用self.key = value,也可用self.__dict__[key]=value 前提没有__slots__
def eat(self):
print('eat method is running')
human = Human()
human.score = 80
#print(human.score) # 报错AttributeError: 'Human' object has no attribute 'score'
human.id = 11010
print(human.id)
# 输出结果:
>>>>>11010
__delattr__
:
触发时机:删除对象成员的时候自动触发
作用:可以限制删除对象成员或者在删除时执行额外的工作
参数:self接收当前对象,第二个参数接收删除的成员名称字符串 (object.__delattr__(self, name)
)
返回值:可有可无,无的话则不会执行删除操作
注意事项:在该魔术方法中,禁止使用del 当前对象.成员
的方式(例如:del self.name
)删除成员,否则会发生递归次数过大的错误RecursionError: maximum recursion depth exceeded while calling a Python object
class Human:
def __init__(self):
self.name = 'Python'
self.sex = 'male'
self.age = 18
def __delattr__(self, item):
if item == 'name': # 当删除的对象成员名称为name时,不执行删除操作,即限制了删除name成员
pass
else:
# del self.sex 如果执行此行,则会报错,递归数过大
return super().__delattr__(item)
def eat(self):
print('eat method is running')
human = Human()
del human.name # 不会删除name属性
del human.sex # 会删除sex属性
print(human.name)
print(human.sex)
# 输出结果:
Python
报错AttributeError: 'Human' object has no attribute 'sex'
参考来源:https://www.jianshu.com/p/892792cb774d
https://www.cnblogs.com/wangchengpei/p/11116546.html