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__()抛出的AttributeErrorproperty中的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 (描述符),推荐阅读官网的一个指南: 描述器使用指南