Python中几种属性访问的区别与用法详解
作者:栖迟於一丘 发布时间:2022-12-24 23:36:20
起步
在Python中,对于一个对象的属性访问,我们一般采用的是点(.)属性运算符进行操作。例如,有一个类实例对象foo,它有一个name属性,那便可以使用foo.name对此属性进行访问。一般而言,点(.)属性运算符比较直观,也是我们经常碰到的一种属性访问方式。
python的提供一系列和属性访问有关的特殊方法: __get__ , __getattr__ , __getattribute__ , __getitem__ 。本文阐述它们的区别和用法。
属性的访问机制
一般情况下,属性访问的默认行为是从对象的字典中获取,并当获取不到时会沿着一定的查找链进行查找。例如 a.x 的查找链就是,从 a.__dict__['x'] ,然后是 type(a).__dict__['x'] ,再通过 type(a) 的基类开始查找。
若查找链都获取不到属性,则抛出 AttributeError 异常。
__getattr__ 方法
__getattr__函数的作用: 如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底。
这个方法是当对象的属性不存在是调用。如果通过正常的机制能找到对象属性的话,不会调用 __getattr__ 方法。
class A:
a = 1
def __getattr__(self, item):
print('__getattr__ call')
return item
t = A()
print(t.a)
print(t.b)
# output
1
__getattr__ call
b
__getattribute__ 方法
这个方法会被无条件调用。不管属性存不存在。如果类中还定义了 __getattr__ ,则不会调用 __getattr__() 方法,除非在 __getattribute__ 方法中显示调用 __getattr__() 或者抛出了 AttributeError 。
class A:
a = 1
def __getattribute__(self, item):
print('__getattribute__ call')
raise AttributeError
def __getattr__(self, item):
print('__getattr__ call')
return item
t = A()
print(t.a)
print(t.b)
所以一般情况下,为了保留 __getattr__ 的作用, __getattribute__() 方法中一般返回父类的同名方法:
def __getattribute__(self, item):
return object.__getattribute__(self, item)
使用基类的方法来获取属性能避免在方法中出现无限递归的情况。
__get__ 方法
这个方法比较简单说明,它与前面的关系不大。
如果一个类中定义了 __get__() , __set__() 或 __delete__() 中的任何方法。则这个类的对象称为描述符。
class Descri(object):
def __get__(self, obj, type=None):
print("call get")
def __set__(self, obj, value):
print("call set")
class A(object):
x = Descri()
a = A()
a.__dict__['x'] = 1 # 不会调用 __get__
a.x # 调用 __get__
如果查找的属性是在描述符对象中,则这个描述符会覆盖上文说的属性访问机制,体现在查找链的不同,而这个行文也会因为调用的不同而稍有不一样:
如果调用是对象实例(题目中的调用方式), a.x 则转换为调用: 。 type(a).__dict__['x'].__get__(a, type(a))
如果调用的是类属性, A.x 则转换为: A.__dict__['x'].__get__(None, A)
其他情况见文末参考资料的文档
__getitem__ 方法
这个调用也属于无条件调用,这点与 __getattribute__ 一致。区别在于 __getitem__ 让类实例允许 [] 运算,可以这样理解:
__getattribute__ 适用于所有 . 运算符;
__getitem__ 适用于所有 [] 运算符。
class A(object):
a = 1
def __getitem__(self, item):
print('__getitem__ call')
return item
t = A()
print(t['a'])
print(t['b'])
如果仅仅想要对象能够通过 [] 获取对象属性可以简单的:
def __getitem(self, item):
return object.__getattribute__(self, item)
总结
当这几个方法同时出现可能就会扰乱你了。我在网上看到一份示例还不错,稍微改了下:
class C(object):
a = 'abc'
def __getattribute__(self, *args, **kwargs):
print("__getattribute__() is called")
return object.__getattribute__(self, *args, **kwargs)
# return "haha"
def __getattr__(self, name):
print("__getattr__() is called ")
return name + " from getattr"
def __get__(self, instance, owner):
print("__get__() is called", instance, owner)
return self
def __getitem__(self, item):
print('__getitem__ call')
return object.__getattribute__(self, item)
def foo(self, x):
print(x)
class C2(object):
d = C()
if __name__ == '__main__':
c = C()
c2 = C2()
print(c.a)
print(c.zzzzzzzz)
c2.d
print(c2.d.a)
print(c['a'])
可以结合输出慢慢理解,这里还没涉及继承关系呢。总之,每个以 __ get 为前缀的方法都是获取对象内部数据的钩子,名称不一样,用途也存在较大的差异,只有在实践中理解它们,才能真正掌握它们的用法。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。
来源:http://www.hongweipeng.com/index.php/archives/1625/


猜你喜欢
- 很多应用多需要处理文件,而处理文件有一个固定的模式:打开文件,读入一些数据,处理这些数据,打印到屏幕上或写入另一个文件。那么,如果我们想修改
- 前言Python 在 2.6 版本中新加了一个字符串格式化方法: str.format() 。它的基本语法是通过 {} 和 : 来代替以前的
- 数据库缓存为了使用数据库表作为缓存后端,首先在数据库中运行这个命令以创建缓存表:python manage.py createcacheta
- 绘制一个线框图是你在制作一个网站前必须要经历的过程。线框图能够帮助你合理的组织并简化你内容和元素,是网站内容布局的基本视觉表现方式,是网站开
- 使用Python+OpenCV实现鼠标画框的代码,供大家参考,具体内容如下# -*-coding: utf-8 -*-""
- WEB标准,从我大二开始接触到毕业后的第一份工作“页面重构工程师”,从接触标准到蓝色理想标准区版主的四年多时间里,WEB标准已经成为我生活中
- 本文通过实例代码介绍了如何在jscript和vbscript中使用操作FileSystemObject(fso)对象模式来编程.
- 1.启动数据库命令行客户端#linux命令,注意区分大小写mysql2.查询数据库#执行结果:返回所有数据库列表SHOW DATABASES
- 交互式命令行是一种方便用户进行交互的工具,能够使用户与计算机进行快速的交互操作,提高工作效率。Python pyreadline模块可以帮助
- Django cors跨域问题前后端分离项目中的跨域问题 即同源策略同源策略:同源策略/SOP(Same origin policy)是一种
- 在网页局部的布局上,需要采用大量的自适应,来满足不同长度数据、内容的合理呈现。以此保证页面不至于因为内容过多等原因,导致重叠、溢出、撑开等破
- 0、什么时候会用到virtualenv?假设系统中的两个应用,其中A应用对库LibFoo的版本要求为1,而B应用对同一个库LibFoo的版本
- 是否看见大站的广告都是放在内容中间实现文字环绕的呢,一般普通小站广告只能放在内容开头或者结尾,也许大站的cms系统带这个功能吧,我们小站常用
- 本文实例讲述了Python反射和内置方法重写操作。分享给大家供大家参考,具体如下:isinstance和issubclassisinstan
- 在实际的机器学习项目中,我们通常需要将训练好的模型保存到磁盘,以便在以后的时间点进行推理或重新训练。同样地,我们也需要从磁盘加载模型以供使用
- 1.后台action产生json数据。List blackList = blackService.getBlackInfoList(mobi
- SQL SERVER用来判断表或视图存在的语句在ORACLE中不能用,请问该怎么写。谢谢。答案exists(select tnam
- time模块下有两种时间表示方法:第1种是:时间戳的方式。是基于1970年1月1日0时0分0秒的偏移。浮点数。第2种是:struct_tim
- 读写分离,顾名思义,我们可以把读和写两个操作分开,减轻数据的访问压力,解决高并发的问题。那么我们今天就Python两大框架来做这个读写分离的
- 本篇文章记录如何使用python将pdf文件切分成一张一张图片,包括环境配置、版本兼容问题。环境配置(mac)安装ImageMagickbr