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

廖雪峰Python教程1轮还没学明白的(3) -- 面向对象高级编程 -- 定制类 -- __getattr__之链式调用

2017-06-21 22:58 1061 查看
原文链接:
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319098638265527beb24f7840aa97de564ccc7f20f6000#0


__getattr__

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义
Student
类:
class Student(object):

def __init__(self):
self.name = 'Michael'


调用
name
属性,没问题,但是,调用不存在的
score
属性,就有问题了:
>>> s = Student()
>>> print(s.name)
Michael
>>> print(s.score)
Traceback (most recent call last):
...
AttributeError: 'Student' object has no attribute 'score'


错误信息很清楚地告诉我们,没有找到
score
这个attribute。

要避免这个错误,除了可以加上一个
score
属性外,Python还有另一个机制,那就是写一个
__getattr__()
方法,动态返回一个属性。修改如下:
class Student(object):

def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99


当调用不存在的属性时,比如
score
,Python解释器会试图调用
__getattr__(self,
'score')
来尝试获得属性,这样,我们就有机会返回
score
的值:
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99


返回函数也是完全可以的:
class Student(object):

def __getattr__(self, attr):
if attr=='age':
return lambda: 25


只是调用方式要变为:
>>> s.age()
25


注意,只有在没有找到属性的情况下,才调用
__getattr__
,已有的属性,比如
name
,不会在
__getattr__
中查找。

此外,注意到任意调用如
s.abc
都会返回
None
,这是因为我们定义的
__getattr__
默认返回就是
None
。要让class只响应特定的几个属性,我们就要按照约定,抛出
AttributeError
的错误:
class Student(object):

def __getattr__(self, attr):
if attr=='age':
return lambda: 25raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)


这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。

举个例子:

现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:
http://api.server/user/friends
http://api.server/user/timeline/list

如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

利用完全动态的
__getattr__
,我们可以写出一个链式调用:
class Chain(object):

def __init__(self, path=''):
self._path = path

def __getattr__(self, path):
return Chain('%s/%s' % (self._path, path))

def __str__(self):
return self._path

__repr__ = __str__


试试:
>>> Chain().status.user.timeline.list
'/status/user/timeline/list'


这样,无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!

还有些REST API会把参数放到URL中,比如GitHub的API:
GET /users/:user/repos


调用时,需要把
:user
替换为实际用户名。如果我们能写出这样的链式调用:
Chain().users('michael').repos


就可以非常方便地调用API了。有兴趣的童鞋可以试试写出来。

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