__getattr__动态获取接口
# -*- coding:utf-8 -*-
#在看廖雪峰的python3.5教学时,看到面向对象高级编程_定义类
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319098638265527beb24f7840aa97de564ccc7f20f6000
百度了很久没有找到想要的答案,想了很久,总结了一点点心得,写的不好,希望大家指正
途中发现好多问题,包括前面没有理解的,停留了好久
##################################
#正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义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: 25
raise 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'
#==========================================
#这里想了很久;查看一个类
#创建了一个空的类,再创建一个实例实例
class Student(object):
pass
#bart = Student()
#变量bart指向的就是一个Student的实例
#再看刚刚那个例子,Chain()表示类自己(本身);
#Chain().status表示的类Chain()的status属性,此时函数就去寻找Chain的status属性
#但发现没有此属性,这时特殊函数__getattr__起作用了,它帮我们创建属性status属性
#根据定义的方法将新的属性前加一个'/'
#初始化使用了默认参数path为空
#def __init__(self, path=''):
#连贯起来就是
#1.先定义类Chain()的一个属性path,切默认值为'',为空
#2.定义方法,特殊函数__getattr__遇到未定义的属性默认返回空,不报错
#由于定义了方法,所以返回return Chain('%s/%s' % (self._path, path))
#即将未定义的额属性以'/'为分隔符输出
#3.方法def __str__(self):的作用是打印字符串
#当然还有细节没有搞清楚
#1.class中的各方法参数是表示同一个值吗?
#即三个方法中的self._path相关么?
"世界上只有一种真正的英雄主义,就是认清了生活的真相后,还依然执着地热爱它。"
——罗曼·罗兰