python中类实例属性的访问以及__getattr__和__getattribute__的方法说明

一、文章概述

本文基于python官方文档理解两种__getattr__和__getattibute__自定义方法的访问机制

阅读本文章,你可以了解到:

  1. 获取python类实例属性的三种方式
  2. __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__()之前,先来探索一下访问类实例对象访问属性的几种方式:

  1. @property:使用@property装饰器操作类属性
  2. obj.name:通过类对象访问获取name属性的值
  3. 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'

官方文档给出的解释
图片来源python官方文档:docs-pdf/library.pdf(第22页)
从文档可以很明显的看出两者访问方式的区别仅仅是getattr()方法中的default值的设定:

如果使用getattr(obj, name[, default])时设置了default值,则不会发生AttributeError异常,而返回值default
(当然是在类中未自定义__getattr__和__getattribute__方法的前提下)

三、getattrgetattribute

下面我们就来研究一下__getattr__和__getattribute__方法

方法的标准定义:
object.__getattr__(self, name)
object.__getattribute__(self, name)

官方文档对__getattr__()和__getattribute__()的说明:
图片来源python官方文档:docs-pdf/reference.pdf(第32页)
根据文档对__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. 文档扩展说明

图片来源python官方文档:docs-pdf/reference.pdf(第32页)如下代码将会发生无线递归:

# ...
class Gsd(object):
    # ...
    def __getattribute__(self, name):
        return self.__getattr__(name)

总结

  1. 获取类实例属性的三种方式:
    1). @property:使用@property装饰器操作类属性
    2). obj.name:通过类对象访问获取name属性的值
    3). getattr(object, name[,default]):getattr()方法获取

obj.name和getattr()方法等效,区别与getattr()方法的default值。如果设置了default值,则getattr()方法可以获取未定义的类实例属性的值:default

  1. __getattr__()和__getattribute__()可重写
    访问属性时,优先调用__getattribute__()方法。该方法发生AttributeError异常时调用__getattr__()方法

相关内容

关于setattr(object, key, value)和__setattr__(self, key, value),参考链接: python魔法方法之__setattr__()

相关资料

链接: python官方文档(3.8)


以上就是访问类实例属性以及__getattr__()和__getattribute__()方法的访问机制全部内容了

学者能力尚浅,如有建议或意见,欢迎指正批评

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值