1
2
3
4
5
6
7
8
|
class A:
def __init__(self, name):
self.name = name
a = A('foo')
print(a.name)
print(a.age)
|
输出:
1
2
3
4
5
|
foo
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/test/test.py", line 8, in <module>
print(a.age)
AttributeError: 'A' object has no attribute 'age'
|
- 访问属性时,
__getattribute__(self, name)
总是优先被调用
- 当访问一个不存在的实例属性时
__getattribute__(self, name)
就会抛出AttributeError
异常,然后尝试用__getattr__()
访问这个属性
__getattr__()
被调用的情况:(a)当需要访问的属性不在实例的__dict__
中;(b)也不在其父类和祖先类的__dict__
中;(c)AttributeError
被触发(包括__getattribute__()抛出的AttributeError
,property
中的get()
方法触发的AttributeError
两种情况)。除此之外除非显式调用,否则不会被调用。
如何正确的重写__getattribute__()
#
一个错误的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
pass
def __getattribute__(self, item):
return self.__dict__.get(item)
a = A('foo')
print(a.name)
print(a.age)
|
输出:
1
2
3
4
5
6
7
8
9
10
11
|
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/test/test.py", line 13, in <module>
print(a.name)
File "C:/Users/Administrator/Desktop/test/test.py", line 9, in __getattribute__
return self.__dict__.get(item)
File "C:/Users/Administrator/Desktop/test/test.py", line 9, in __getattribute__
return self.__dict__.get(item)
File "C:/Users/Administrator/Desktop/test/test.py", line 9, in __getattribute__
return self.__dict__.get(item)
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
|
这是因为self.__dict__.get()
仍旧调用的是__getattribute__
, 造成循环调用
正确的写法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class A:
def __init__(self, name):
self.name = name
def __getattr__(self, item):
raise AttributeError
def __getattribute__(self, item):
return super(A, self).__getattribute__(item)
a = A('foo')
print(a.name)
print(a.age)
|
输出:
1
2
3
4
5
6
7
|
foo
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/test/test.py", line 14, in <module>
print(a.age)
File "C:/Users/Administrator/Desktop/test/test.py", line 6, in __getattr__
raise AttributeError
AttributeError
|
除此之外,需要注意,访问未定义属性从而调用到__getattr__()
时,需要在__getattr__()
中手动抛出AttributeError
,否则会返回None
。
property, getattribute, __getattr__三者的访问顺序#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class A:
def __init__(self):
self.name = None
def __getattr__(self, item):
print('call at __getattr__')
raise AttributeError
def __getattribute__(self, item):
print('call at __getattribute__', item)
return super(A, self).__getattribute__(item)
@property
def a(self):
print('call at property')
return self.name
@a.setter
def a(self, value):
self.name = value
a = A()
a.a = 'foo'
print(a.a)
|
输出:
1
2
3
4
|
call at __getattribute__ a
call at property
call at __getattribute__ name
foo
|
- 仍旧是先访问
__getattribute__
, 然后再访问property
, property
中访问name
会再调用一次__getattribute__
property
相关内容涉及到python descriptor (描述符),推荐阅读官网的一个指南: 描述器使用指南