链式调用拆解分析(小白式理解)
Topic source顺便也厘清了一些迷惑,class(arg)是创建实例,arg和class的__init__方法中确定的参数个数相匹配,而class()(arg)是创建一个实例以后再调用这个实例,需要有__call__方法支持。这是完成例子中实现的代码:
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
def __call__(self,path):
return Chain('%s/:%s' % (self._path, path))
在这个例子中__getattr__和__call__的功能都是拼接,区别仅仅是__call__里面多了一个冒号:
其实你们的做法全部都是错误的,在让你们做一下这个试题的时候,还没讲 __call__ 方法呢
Chain().users('michael').repos
正确的解答方法是:
class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): if(path =='users'): return Chain return Chain('%s/%s' % (self._path, path)) def __str__(self): return self._path __repr__ = __str__
- 1
__MAY21
1.第一步
In [1]:
Out[1]:
创建实例Chain(),由于没有传入 path参数,默认参数 path="",所以直接打印实例结果也为 空。
2.第二步
In [2]:
Out[2]:
尝试调用实例Chain()的users属性,由于users属性没有定义,于是调用__getattr__()方法。
根据Chain类的定义,进入__getattr__()内部后执行Chain(),传入参数"self.__path/path"(这里的 self.__path为第一步默认值"",path为第二步尝试调用的"users"),即拼接成"/users",也就是执行:Chain("/users"),这相当于在类的内部实例化自己然后传入参数"/users"。
也就是说,在执行Chain().users时,由于实例没有定义users这个属性,于是调用__getattr__()方法,最终执行的是Chain("/users"),结果就打印出/users。
3.第三步
In [3]:
Out[3]:
由于定义了__call__()方法,使得可以将对象实例当作方法一样调用。
第二步中,执行Chain().users相当于实例化了一个对象:Chain("/users")。于是第三步就相当于,把这个对象实例当做方法直接调用:Chain("/users")(),并传入参数"michael",此时执行的就是 __call__()方法。
同理,根据Chain类的定义,进入__call__()内部后,执行Chain(),传入参数"self.__path/path"(这里的self.__path为第二步的结果"/users",path为第三步传入的"michael"),即拼接成"/users/michael",也就是执行:Chain("/users/michael")。
4.第四步
In [4]:
Out[4]:
第三步的最后相当于执行对象实例Chain("/users/michael"),那么第四步,就在这个实例基础上尝试调用repos属性,显然repos属性没有被定义,于是再次调用__getattr__()方法。然后就重复第二步的逻辑,最终完成拼接/users/michael/repos。