python3完全使用了新式类,废弃了旧式类,getattribute作为新式类的一个特性有非常奇妙的作用。查看一些博客和文章后,发现想要彻底理解getattr和getattribute的区别,实际上需要理解python中属性的查找顺序、描述器(descriptor)、__get__、__set__、__dict__等知识。

首先需要指出,如果一个类只定义了__get__方法则称之为non-data descriptor(非资料描述器),如果这个类将__get__和 __set__都定义了,则称之为data descriptor(资料描述器),具体可见我的另一篇博客,方便理解。下面介绍实例属性的查找顺序。假设 t = T(),t.at的查找顺序如下:

1、如果是python自动产生的属性,返回,否则进行第2步

2、如果‘at’出现在了T或者其父类和祖先类__dict__中(即‘at’是一个类属性,并非只属于t的实例属性),并且at是一个data descriptor,则优先调用其__get__方法。不是data descriptor或者没有该属性则进行第2步。

3、查找实例t的__dict__中是否有at属性,有则返回,没有则到第3步。

4、查找t的父类和祖先类的__dict__中是否有at属性,如果没有则执行第4步,如果有则执行如下步骤:

​ 4.1 at是一个non-data descriptor,调用其__get__方法,不是则执行3.2

​ 4.2 返回__dict__['at']

5、如果实例t的父类中有__getattr__方法,则调用该方法,没有则抛出AttributeError。

注意每次类或实例调用属性时getattribute会被无条件首先调用。下方代码略长,耐心查看。

class Descriptor:  # 定义描述器的类
def __get__(self, instance, owner): # get方法用于返回实例的a属性
print('3 get called,', 'instance is', instance, ',owner is', owner)
return instance.a def __set__(self, instance, value): # set方法用于修改实例的a属性
print('4 set called,', 'instance is', instance, ',value is', value)
instance.a = value**2 def __getattribute__(self, item):
print('5 Des getattribute called, item is %s' % item) class NotDescriptor: # 定义non-data descriptor
def __get__(self, instance, owner):
print('6 get called,', 'instance is', instance, ',owner is', owner)
return instance.a - 100 class T:
desc = Descriptor() # 类属性,一个资料描述器 data descriptor
Not_Desc = NotDescriptor() # 类属性,一个非资料描述器 non-data descriptor
def __init__(self):
self.a = 123
self.not_desc = 'instance not_desc' def func(self):
return 'T func' def __getattribute__(self, item):
print('1 getattribute called, item is ', item)
return object.__getattribute__(self, item) def __getattr__(self, item):
print('2 getattr called, item is', item)
raise AttributeError('NO such attr %s' % item)

以下的测试均使用上方的代码,每次的输出操作都重置过,以避免混淆。

1 getattribute called, item is  func
T func # 实例的函数
1 getattribute called, item is a
123 # 实例的属性
==================================================
4 set called, instance is <__main__.T object at 0x000001E55B630240> ,value is 2
# set方法前不调用getattribute。instance是拥有该描述器类的一个实例。value是要设置的值。
==================================================
1 getattribute called, item is desc
3 get called, instance is <__main__.T object at 0x000001E55B630240> ,owner is <class '__main__.T'>
# get方法前还是优先调用getattribute,instance是拥有该描述器对象的一个实例。owner是拥有者本身
1 getattribute called, item is a
t.a = 4 # 2**2 = 4

可见调用实例的属性和方法时都会无条件首先调用getattrtibute方法,而set方法则不会调用。另外代码利用描述器的get和set方法,实际上已经实现了类似于@property的使用(当然,property装饰器其实就是基于描述器实现的,对property装饰器不了解的话可以查看我的另一篇博客)。

下面继续使用上方代码验证__getattr__、非描述器的执行顺序。

t = T()
print(t.not_desc)
--------结果如下------
1 getattribute called, item is not_desc
instance not_desc

回想前面提到的属性查找顺序,‘not_desc’在类的__dict__被找到了,但不是descriptor,所以执行第2步,在实例的__dict__中发现该属性,返回。

t = T()
print(t.Not_Desc)
--------结果如下------
1 getattribute called, item is Not_Desc
6 get called, instance is <__main__.T object at 0x0000017F70A006A0> ,owner is <class '__main__.T'>
# 这里是NonDataDescriptor中的get方法
1 getattribute called, item is a
23 # a的初始值为123 由get方法返回的是123-100=23

根据我们之前提到的顺序,‘Not_Desc’属性在类的__dict__中找到但不是data descriptor,又没有在实例的dict中找到,所以进行到了4.1步骤,发现它是一个non-data descriptor,然后就执行了其中的get方法。

t = T()
print(t.bbbbbb) # T类中没有定义该属性
------结果如下----------
1 getattribute called, item is bbbbbb
2 getattr called, item is bbbbbb
Traceback (most recent call last):
File "C:/省略/.py", line 40, in <module>
print(t.bbbbbb)
File "C:C:/省略/.py.py", line 36, in __getattr__
raise AttributeError('NO such attr %s' % item)
AttributeError: NO such attr bbbbbb

可见,当我们访问一个没有被定义的属性时,仍然会首先调用getattribute,根据属性查找原则,在实例和类中都没有找到这个属性,于是执行getattr。到这里我们也就了解到了python中属性的查找顺序、getattribute和getattr的执行顺序。

回过头来,getattribute会在调用类和实例的属性时无条件调用,所以可以用于权限鉴别、日志记录等操作;而getattr会在属性没有被找到的时候执行,因此可以用来做一些兜底的操作,可见这篇博客

另外注意重写getattribute时的循环陷阱,返回语句要写成return object.__getattribute__(),不要使用return self.xxx,否则self相当于又指向了自己,而getattribute会无条件调用,从而进入无限循环。


最后的我们查看类和实例的__dict__属性,看看又什么不同

t = T()
print(t.__dict__)
print(T.__dict__)
------结果如下--------
1 getattribute called, item is __dict__
{'a': 123, 'not_desc': 'instance not_desc'}
{'__module__': '__main__', 'desc': <__main__.Descriptor object at 0x0000021FD20C0128>, 'Not_Desc': <__main__.NotDescriptor object at 0x0000021FD20C0160>, '__init__': <function T.__init__ at 0x0000021FD20B59D8>, 'func': <function T.func at 0x0000021FD20B5A60>, '__getattribute__': <function T.__getattribute__ at 0x0000021FD20B5AE8>, '__getattr__': <function T.__getattr__ at 0x0000021FD20B5B70>, '__dict__': <attribute '__dict__' of 'T' objects>, '__weakref__': <attribute '__weakref__' of 'T' objects>, '__doc__': None}

可见实例t只有init中定义的属性,而类T中才有描述器和非描述器等属性。

参考:

https://blog.csdn.net/yitiaodashu/article/details/78974596

https://www.cnblogs.com/Vito2008/p/5280216.html

https://www.cnblogs.com/pyxiaomangshe/p/7927540.html

python-__getattr__ 和 __getattribute__的更多相关文章

  1. Python - __getattr__和__getattribute__的区别

    传送门 https://docs.python.org/3/reference/datamodel.html#object.__getattr__ https://docs.python.org/3/ ...

  2. python __getattr__和 __getattribute__

    __getattr__ 这个魔法函数会在类中查找不到属性时调用 class User: def __init__(self): self.info = 1 def __getattr__(self, ...

  3. Python的__getattr__和__getattribute__

    __getattr____getattr__在当前主流的Python版本中都可用,重载__getattr__方法对类及其实例未定义的属性有效.也就属性是说,如果访问的属性存在,就不会调用__getat ...

  4. python魔法方法:__getattr__,__setattr__,__getattribute__

    python魔法方法:__getattr__,__setattr__,__getattribute__ 难得有时间看看书....静下心来好好的看了看Python..其实他真的没有自己最开始想的那么简单 ...

  5. 浅谈Python 中 __getattr__与__getattribute__的区别

    __getattr__与__getattribute__均是一般实例属性截取函数(generic instance attribute interception method),其中,__getatt ...

  6. python中的__getattr__、__getattribute__、__setattr__、__delattr__、__dir__

    __getattr__:     属性查找失败后,解释器会调用 __getattr__ 方法. class TmpTest: def __init__(self): self.tmp = 'tmp12 ...

  7. 一些代码 II (ConfigParser、创建大文件的技巧、__getattr__和__getattribute__、docstring和装饰器、抽象方法)

    1. ConfigParser format.conf [DEFAULT] conn_str = %(dbn)s://%(user)s:%(pw)s@%(host)s:%(port)s/%(db)s ...

  8. python魔法函数之__getattr__与__getattribute__

    getattr 在访问对象的属性不存在时,调用__getattr__,如果没有定义该魔法函数会报错 class Test: def __init__(self, name, age): self.na ...

  9. Python魔法方法__getattr__和__getattribute__详解

    在Python中有这两个魔法方法容易让人混淆:__getattr__和getattribute.通常我们会定义__getattr__而从来不会定义getattribute,下面我们来看看这两个的区别. ...

  10. python __getattr__ & __getattribute__ 学习

    实例属性的获取和拦截, 仅对实例属性(instance, variable)有效, 非类属性 getattr: 适用于未定义的属性, 即该属性在实例中以及对应的类的基类以及祖先类中都不存在 1. 动态 ...

随机推荐

  1. linux 下线程错误查找,与线程分析命令

    一. 使用top和jstack查找线程错误 我们使用jdk自带的jstack来分析.当linux出现cpu被java程序消耗过高时,以下过程说不定可以帮上你的忙: 1.top查找出哪个进程消耗的cpu ...

  2. Asp.net MVC使用FormsAuthentication,MVC和WEB API可以共享身份认证 (转载)

    在实际的项目应用中,很多时候都需要保证数据的安全和可靠,如何来保证数据的安全呢?做法有很多,最常见的就是进行身份验证.验证通过,根据验证过的身份给与对应访问权限.同在Web Api中如何实现身份认证呢 ...

  3. Notes 20180505 : 计算机的基础知识

    总是想要去深入了解一下计算机,可真正去了解的时候才发现那并非一日之功,关于计算机的学习,并未放弃,但是化知识为笔记尚需时日,今日我们先简单了解一下计算机,然后开始Java语言的学习. 1 计算机的基础 ...

  4. DG不同步,MRP0进程打不开

    问题描述:主库备库之前正常连接,但是昨天磁盘空间满了之后,由于不知什么原因将备库重做日志删了,今天早上发现DG不同步的报警. 当时思路如下:1.通过select thread#,low_sequenc ...

  5. UGA,PGA

    tom认为UGA不包含 sort工作区,所以下面的图都是错误的 The UGA is, in effect, your session’s state. It is memory that your ...

  6. Linux-- 文件编辑器 vi/vim(1)

    初识 vi/vim 文本编辑器 1.vi 和 vim 相同,都是文本编辑器,在 vi 模式下可以查看文本,编辑文本,是 Linux 最常用的命令,vi 模式下分为三部分,第一部分一般模式,在一般模式中 ...

  7. ios中input输入无效

    项目中一个登陆界面的input在安卓下可以输入,iOS下无法输入,经查询为 设置了-webkit-user-select:none;将其改为-webkit-user-select:auto;修正. 参 ...

  8. nodejs知识点

    rss(resident set size):所有内存占用,包括指令区和堆栈. heapTotal:”堆”占用的内存,包括用到的和没用到的. heapUsed:用到的堆的部分. external: V ...

  9. scala (2) while 和变量

    (1)在scala中声明变量有两个关键字,val和var val: 是不可变的,即声明了变量不能再进行更改,类似于java中的final var: 是可变的,即可以重新对其赋值 声明变量的通用格式:  ...

  10. DP_最长公共子序列/动规入门

    学自:https://open.163.com/movie/2010/12/L/4/M6UTT5U0I_M6V2U1HL4.html 最长公共子序列:(本文先谈如何求出最长公共子序列的长度,求出最长公 ...