在之前的文章有提到__getattr__函数的作用: 如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底。
我们来看几个例子:
第一个例子,比如我业务里有好几个request请求,但是你会发现所有的请求无非都是请求参数不一样,仅此而已,那么通常你需要定义好多个方法,但是方法里的逻辑都几乎一样,那怎么去简化呢,我这里给个例子
from functools import partial
IAM = {
"path": "/api/iam/",
"actions": {
"get_auth_info": ("GET", "GetAuthInfo"),
"policy_check": ("GET", "PolicyCheck"),
"get_user": ("GET", "GetUser"),
"create_resource_group": ("POST", "CreateResourceGroup"),
"list_resource_group": ("GET", "ListResourceGroups"),
"list_policy_attachments": ("GET", "ListPolicyAttachments"),
"get_policy": ("GET", "GetPolicy"),
"create_policy": ("GET", "CreatePolicy"),
"attach_policy": ("POST", "AttachPolicy"),
"create_policy_version": ("POST", "CreatePolicyVersion"),
"set_default_policy_version": ("POST", "CreatePolicyVersion"),
"list_users": ("GET", "ListUsers"),
"list_policies": ("GET", "ListPolicies")
}
}
RMS = {
"path": "/api/rms/",
"actions": {
"list_regions": ("GET", "ListRegions")
}
}
SERVICE_MAP = {
"IAM": IAM,
"RMS": RMS
}
class Client():
def __init__(self, endpoint, service_name):
self.service_name = service_name
service = SERVICE_MAP[self.service_name]
self.path = service['path']
self.actions = service['actions']
def common_method(self, method, action, filters):
# 这里写的是通用的方法逻辑,比如调用request请求
print(method)
print(action)
print(filters)
def __getattr__(self, name):
method, action = self.actions.get(name, (None, None))
if method:
return partial(self.common_method, method, action)
dbfilters = {
'tt1': '111', 'tt2': '222'}
myclient = Client('http://127.0.0.1:8888', 'IAM')
# 这里的方法在Client类中没有定义,其实就是走了 __getattr__ 这个方法,name这个字段就是get_auth_info
myclient.get_auth_info(filters=dbfilters)
运行结果如下:
可以看到通过这个方法名,对应匹配到了actions中的数据
第二个例子,我觉的是最实用的,__getattr__使得实现adapter wrapper模式非常容易,我们都知道“组合优于继承”,__getattr__实现的adapter就是以组合的形式。
class adaptee(object):
def foo(self):
print 'foo in adaptee'
def bar(self):
print 'bar in adaptee'
class adapter(object):
def __init__(self):
self.adaptee = adaptee()
def foo(self):
print 'foo in adapter'
self.adaptee.foo()
def __getattr__(self, name):
return getattr(self.adaptee, name)
if __name__ == '__main__':
a = adapter()
a.foo()
a.bar()
如果adapter需要修改adaptee的行为,那么定义一个同名的属性就行了,其他的想直接“继承”的属性,通通交给__getattr__就行了。