Python魔法方法之__getattr__和getattribute

发布时间:2023年12月31日

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

__getattr__魔法方法

class?MyClass:
????def?__init__(self,?x):
????????self.x?=?x
????def?__getattr__(self,?item):
????????print('{}属性为找到!'.format(item))
????????return?None
>>>?obj?=?MyClass(1)
>>>?obj.x
1
>>>?obj.y
y属性为找到!
None

我们定义一个MyClass类,设置一个实例属性为x,值为1。obj为这个类的实例,获取obj.x返回1,而获取obj.y发现属性找不到,原因是obj的实例变量中不包含y,找不到某属性时会调用__getattr__方法。

**调用__getattr__详细过程如下:**

obj.attr

1.首先会在对象的实例属性中寻找,找不到执行第二步

2.来到对象所在的类中查找类属性,如果还找不到执行第三步

3.来到对象的继承链上寻找,如果还找不到执行第四步

4.调用obj.__getattr__方法,如果用户没有定义或者还是找不到,抛出AttributeError异常,属性查找失败!

class?MyClass:
????def?__init__(self,?x):
????????self.x?=?x
>>>?obj?=?MyClass(1)
>>>?obj.y
AttributeError:?'MyClass'?object?has?no?attribute?'a'

如上代码,没有定义__getattr__魔法方法,又找不到属性,就会抛出异常。

__getattribute__魔法方法

当我们调用对象的属性时,首先会调用__getattribute__魔法方法。

obj.x
obj.__getattribute__(x)

如上代码,这两个代码其实是等价的。当__getattribute__查找失败,就会去调用__getattr__方法。

代码演示

class?MyClass:
????def?__init__(self,?x):
????????self.x?=?x
????def?__getattribute__(self,?item):
????????print('正在获取属性{}'.format(item))
????????return?super(MyClass,?self).__getattribute__(item)
>>>?obj?=?MyClass(2)
>>>?obj.x
正在获取属性x
2

我们使用__getattribute__魔法方法时,要返回父类的方法,不然很难写对,下面代码是一个陷阱,会产生递归。

class?MyClass:
????def?__init__(self,?x):
????????self.x?=?x
????def?__getattribute__(self,?item):
????????print('正在获取属性{}'.format(item))
????????return?self.item
????????
>>>?obj?=?MyClass(2)
>>>?obj.x
??File?"xxx",?line?11,?in?__getattribute__
????print('正在获取属性{}'.format(item))
RecursionError:?maximum?recursion?depth?exceeded?while?calling?a?Python?object

上面的代码看起来似乎是对的,但却调入了递归的陷阱,相当于

def?__getattribute__(self,?item):
????print('正在获取属性{}'.format(item))
????return?self.__getattribute__(item)

要十分警惕。

另外,内置的getattr和hasattr也会触发这个魔法方法。

>>>?getattr(obj,?'x',?None)
正在获取属性x
2
>>>?hasattr(obj,?'x',?None)
正在获取属性x
True

其他细节需要注意

class?MyClass:
????x?=?999
????
????def?__init__(self,?x):
????????self.x?=?x
????def?__getattribute__(self,?item):
????????print('正在获取属性{}'.format(item))
????????return?super(MyClass,?self).__getattribute__(item)

上面代码中,定义了一个类属性x和一个实例属性x,这两个属性同名,根据Python语法规则,当对象获取属性x的时候,首先会在实例属性中寻找,如果找不到才回去类属性中查找。

>>>?obj?=?MyClass(2)
>>>?print(obj.x)
正在获取属性x
2
>>>?del?obj.x??#删除了实例属性x
>>>?print(obj.x)??#此时访问的是类属性
正在获取属性
999

这样就能印证了上面所说__getattribute__的查找顺序。通常该方法在框架中可能会用到,一般情况下无需使用。

文章来源:https://blog.csdn.net/hakesashou/article/details/135318947
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。