您的位置:首页 > 编程语言 > Python开发

Python魔法方法__getattr__和__getattribute__详解

2019-06-26 17:48 1511 查看

在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__
的查找顺序。通常该方法在框架中可能会用到,一般情况下无需使用。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: