object.__getattr__(self, name)
当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。
object.__getattribute__(self, name)
无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)
object.__get__(self, instance, owner)
如果class定义了它,则这个class就可以称为descriptor。owner是所有者的类,instance是访问descriptor的实例,如果不是通过实例访问,而是通过类访问的话,instance则为None。(descriptor的实例自己访问自己是不会触发__get__,而会触发__call__,只有descriptor作为其它类的属性才有意义。)(所以下文的d是作为C2的一个属性被调用)
- class C(object):
- a = 'abc'
- def __getattribute__(self, *args, **kwargs):
- print("__getattribute__() is called")
- return object.__getattribute__(self, *args, **kwargs)
- # return "haha"
- def __getattr__(self, name):
- print("__getattr__() is called ")
- return name + " from getattr"
- def __get__(self, instance, owner):
- print("__get__() is called", instance, owner)
- return self
- def foo(self, x):
- print(x)
- class C2(object):
- d = C()
- if __name__ == '__main__':
- c = C()
- c2 = C2()
- print(c.a)
- print(c.zzzzzzzz)
- c2.d
- print(c2.d.a)
输出结果是:
- __getattribute__() is called
- abc
- __getattribute__() is called
- __getattr__() is called
- zzzzzzzz from getattr
- __get__() is called <__main__.C2 object at 0x16d2310> <class '__main__.C2'>
- __get__() is called <__main__.C2 object at 0x16d2310> <class '__main__.C2'>
- __getattribute__() is called
- abc
小结:可以看出,每次通过 实例 访问属性,都会经过__getattribute__函数。而当属性不存在时,仍然需要访问__getattribute__,不过接着要访问__getattr__。这就好像是一个异常处理函数。
每次访问descriptor(即实现了__get__的类),都会先经过__get__函数。
需要注意的是,当使用类访问不存在的变量是,不会经过__getattr__函数。而descriptor不存在此问题,只是把instance标识为none而已。
来看看如何自定义实现gettattr让代码更加的动态优雅:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 例子在原来的基础上简化了一下,排除依赖和干扰,详细参见原项目
class
UrlGenerator(
object
):
def
__init__(
self
, root_url):
self
.url
=
root_url
def
__getattr__(
self
, item):
if
item
=
=
'get'
or
item
=
=
'post'
:
print
self
.url
return
UrlGenerator(
'{}/{}'
.
format
(
self
.url, item))
url_gen.users.show.get
>>> http:
/
/
xxxx
/
users
/
show
|
充分利用getattr会在没有查找到相应实例属性时被调用的特点,方便的通过链式调用生成对应的url,源代码中在碰到http method的时候返回一个
可调用的对象更加的优雅,链式的操作不仅优雅而且还能很好的说明调用的接口的意义(restful的接口啦)。
示例
1.__getattr__示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
Test(
object
):
def
__init__(
self
,name):
self
.name
=
name
def
__getattr__(
self
, value):
if
value
=
=
'address'
:
return
'China'
if
__name__
=
=
"__main__"
:
test
=
Test(
'letian'
)
print
test.name
print
test.address
test.address
=
'Anhui'
print
test.address
|
运行结果:
1
2
3
|
letian
China
Anhui
|
如果是调用了一个类中未定义的方法,则__getattr__也要返回一个方法,例如:
1
2
3
4
5
6
7
8
9
|
class
Test(
object
):
def
__init__(
self
,name):
self
.name
=
name
def
__getattr__(
self
, value):
return
len
if
__name__
=
=
"__main__"
:
test
=
Test(
'letian'
)
print
test.getlength(
'letian'
)
|
运行结果: 6
2.__getattribute__示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class
Test(
object
):
def
__init__(
self
,name):
self
.name
=
name
def
__getattribute__(
self
, value):
if
value
=
=
'address'
:
return
'China'
if
__name__
=
=
"__main__"
:
test
=
Test(
'letian'
)
print
test.name
print
test.address
test.address
=
'Anhui'
print
test.address
|
运行结果:
1
2
3
|
None
China
China
|
深入思考
既然能通过定制类的getattr自定义方法来实现一些优雅的功能,自然我们也要对它有一些了解,包括和它相似的自定义方法getattribute
1. 用作实例属性的获取和拦截
当访问某个实例属性时, getattribute会被无条件调用,如未实现自己的getattr方法,会抛出AttributeError提示找不到这个属性,如果自定义了自己getattr方法的话,方法会在这种找不到属性的情况下被调用,比如上面的例子中的情况。所以在找不到属性的情况下通过实现自定义的getattr方法来实现一些功能是一个不错的方式,因为它不会像getattribute方法每次都会调用可能会影响一些正常情况下的属性访问:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
Test(
object
):
def
__init__(
self
, p):
self
.p
=
p
def
__getattr__(
self
, item):
return
'default'
t
=
Test(
'p1'
)
print
t.p
print
t.p2
>>> p1
>>> default
|
2. 自定义getattribute的时候防止无限递归
因为getattribute在访问属性的时候一直会被调用,自定义的getattribute方法里面同时需要返回相应的属性,通过self.__dict__取值会继续向下调用getattribute,造成循环调用:
1
2
3
4
5
6
7
8
9
|
class
AboutAttr(
object
):
def
__init__(
self
, name):
self
.name
=
name
def
__getattribute__(
self
, item):
try
:
return
super
(AboutAttr,
self
).__getattribute__(item)
except
KeyError:
return
'default'
|
这里通过调用绑定的super对象来获取队形的属性,对新式类来说其实和object.__getattribute__(self, item)一样的道理:
默认情况下自定义的类会从object继承getattribute方法,对于属性的查找是完全能用的
getattribute的实现感觉还是挺抽象化的,只需要绑定相应的实例对象和要查找的属性名称就行
3.同时覆盖掉getattribute和getattr的时候,在getattribute中需要模仿原本的行为抛出AttributeError或者手动调用getattr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class
AboutAttr(
object
):
def
__init__(
self
, name):
self
.name
=
name
def
__getattribute__(
self
, item):
try
:
return
super
(AboutAttr,
self
).__getattribute__(item)
except
KeyError:
return
'default'
except
AttributeError as ex:
print
ex
def
__getattr__(
self
, item):
return
'default'
at
=
AboutAttr(
'test'
)
print
at.name
print
at.not_exised
>>>test
>>>
'AboutAttr'
object
has no attribute
'not_exised'
>>>
None
|