python中类实例属性的访问以及__getattr__和__getattribute__的方法说明
一、文章概述
本文基于python官方文档理解两种__getattr__和__getattibute__自定义方法的访问机制
阅读本文章,你可以了解到:
- 获取python类实例属性的三种方式
- __getattr__(self, name)和__getattribute__(self, name)方法的访问机制
开始之前,创建一个示例类,本文所有的方法的探索都基于该类:
# -*- coding: UTF-8 -*-
# filename: py_category.py
class Gsd(object):
'''A simple example class'''
def __init__(self, name: str=None):
self.name = name
在 py_category.py 中定义了一个Gsd类继承了obejct(所有类的基类),类的实例化可以接受一个可选参数name
二、类实例访问属性的几种方式
在了解__getattr__()和__getattribute__()之前,先来探索一下访问类实例对象访问属性的几种方式:
- @property:使用@property装饰器操作类属性
- obj.name:通过类对象访问获取name属性的值
- getattr(object, name[,default]):getattr()方法获取属性的值
本次研究主要对第2,3种方式进行探索
1. @property
暂不作说明
2. obj.name
通过类的实例对象访问实例的name属性值
# -*- coding: UTF-8 -*-
# filename: py_category.py
if __name__ == '__main__':
gsd = Gsd('Bob') # 实例化对象,将 name 属性的值设置为 Bob
#通过 obj.name 获取实例属性
name1 = gsd.name # name1 = 'Bob'
3. getattr(object, name[,default])
getattr()方法获取属性的值
# -*- coding: UTF-8 -*-
# filename: py_category.py
if __name__ == '__main__':
gsd = Gsd('Bob') # 实例化对象,将 name 属性的值设置为 Bob
# 方式3:通过getattr()方法获取实例属性
name2 = getattr(gsd, 'name') # name2 = 'Bob
4. obj.name 和 getattr的关系和区别
两种访问方式是等效的:你可以通过任何一种方式来获取实例属性的值
注意:如果实例对象存在属性,则返回属性的值,不存在则抛出AttributeError异常
# -*- coding: UTF-8 -*-
# filename: py_category.py
if __name__ == '__main__':
gsd = Gsd('Bob') # 实例化对象,将 name 属性的值设置为 Bob
# 方式2:通过类对象访问获取name属性的值
name1 = gsd.name # name1 = 'Bob'
# 方式3:通过getattr(object, name[,default])方法 访问获取 name 属性的值
name2 = getattr(gsd, 'name') # name2 = 'Bob'
name3 = getattr(gsd, 'v') # AttributeError: 'Gsd' object has no attribute 'v'
官方文档给出的解释
从文档可以很明显的看出两者访问方式的区别仅仅是getattr()方法中的default值的设定:
如果使用getattr(obj, name[, default])时设置了default值,则不会发生AttributeError异常,而返回值default
(当然是在类中未自定义__getattr__和__getattribute__方法的前提下)
三、getattr 和 getattribute
下面我们就来研究一下__getattr__和__getattribute__方法
方法的标准定义:
object.__getattr__(self, name)
object.__getattribute__(self, name)
官方文档对__getattr__()和__getattribute__()的说明:
根据文档对__getattribute__(self, name)的阐述,在对类实例属性访问时调用的是该方法,只有该方法主动调用了__getattr__方法或发生了AttributeError时才会调用__getattr__()方法。
在之前的示例中,我们遇到的发生AttributeError异常的情况。当访问实例不存在的属性时,就会触发该异常:
# -*- coding: UTF-8 -*-
# filename: py_category.py
if __name__ == '__main__':
gsd = Gsd('Bob')
name2 = getattr(gsd, 'v') # AttributeError:'Gsd' object has no attribute 'v'
对name2尝试获取gsd的’v’属性时触发AttributeError(getattr()方法未设置default值),此时会尝试调用__getattr__()方法,此方法返回属性值或引发AttributeError异常。因为没有对该方法进行重写,所以同样引发了AttributeError异常
1. 重写__getattr__(self, name)
修改py_category.py,在Gsd类中重写__getattr__方法:
# -*- coding: UTF-8 -*-
# filename: py_category.py
class Gsd(object):
'''A simple example class'''
def __init__(self, name: str=None):
self.name = name
def __getattr__(self, name):
return '__getattr__(): ' + name
if __name__ == '__main__':
gsd = Gsd('Bob')
name1 = gsd.name # 系统默认调用 __getattribute__()方法,返回'Bob'
name2 = getattr(gsd, 'v') # 发生AttributeError异常,调用__getattr__()方法
print(name1) # Bob
print(name2) # __getattr__(): v
可以看到,name1通过__getattribute__()方法获得值:‘Bob’,name2可以通过__getattr__()方法获得值:’__getattr__(): v’
注意:这里的v是实例对象的属性名称,而非属性值
2. 重写__getattribute__(self, name)
修改代码,在Gsd类中重写__getattribute__方法:
# -*- coding: UTF-8 -*-
# filename: py_category.py
class Gsd(object):
'''A simple example class'''
def __init__(self, name: str=None):
self.name = name
def __getattr__(self, name):
return '__getattr__(): ' + name
def __getattribute__(self, name:str):
return '__getattribute__(): ' + name
if __name__ == '__main__':
gsd = Gsd('Bob')
name1 = gsd.name # 系统默认调用 __getattribute__()方法,返回'Bob'
name2 = getattr(gsd, 'v') # 未发生异常,调用__getattribute__()方法
print(name1) # __getattribute__(): name
print(name2) # __getattr__(): v
同样的测试代码,name1获得值:’__getattribute__(): name,name2也可以通过__getattribute__()方法获得值:’__getattribute__(): v’
注意:这里的name和v是实例对象的属性名称,而非属性值
__getattribute__方法并未发生AttributeError异常,是不会调用__getattr__方法的
接下来修改__getattribute__方法的重定义:
# ...
class Gsd(object):
# ...
def __getattribute__(self, name):
return super().__getattribute__(name)
这样访问已定义的属性时可以正常访问到属性值,而访问未定义的属性时,会调用__getattr__方法,返回值或发生AttributeError异常
3. 文档扩展说明
如下代码将会发生无线递归:
# ...
class Gsd(object):
# ...
def __getattribute__(self, name):
return self.__getattr__(name)
总结
- 获取类实例属性的三种方式:
1). @property:使用@property装饰器操作类属性
2). obj.name:通过类对象访问获取name属性的值
3). getattr(object, name[,default]):getattr()方法获取
obj.name和getattr()方法等效,区别与getattr()方法的default值。如果设置了default值,则getattr()方法可以获取未定义的类实例属性的值:default
- __getattr__()和__getattribute__()可重写
访问属性时,优先调用__getattribute__()方法。该方法发生AttributeError异常时调用__getattr__()方法
相关内容
关于setattr(object, key, value)和__setattr__(self, key, value),参考链接: python魔法方法之__setattr__()
相关资料
链接: python官方文档(3.8)
以上就是访问类实例属性以及__getattr__()和__getattribute__()方法的访问机制全部内容了
学者能力尚浅,如有建议或意见,欢迎指正批评