Discuss / Python / __getattr__

__getattr__

Topic source
#__getattr__
class Chain(object):
    name='michael'
    def __init__(self, path=''):
        self._path = path

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

    def __str__(self):
        return self._path

    __repr__ = __str__

print(Chain().users.user.repos)  #return:/users/michael/repos  因为print()里面是一个Chain()object,因此会调用__str__方法,打印出self.path。每一个点都运行一次返回一个Chain对象

missingmaria

#2 Created at ... [Delete] [Delete and Lock User]

我想Chain().users('michael').repos的目的应该是对不同的用户名各自生成不同的path, 所以Chain().users('michael').repos得到/users/michael/repos Chain().users('sam').repos得到/users/sam/repos

直接用原来的code应该就可以实现,而不用设置name='michael': 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__

但我尝试运行了一下却报错,也不知道是哪里理解出了问题

missingmaria

#3 Created at ... [Delete] [Delete and Lock User]

我知道了,加上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

__repr__ = __str__

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

annilq

#4 Created at ... [Delete] [Delete and Lock User]

完美!

houbo111

#5 Created at ... [Delete] [Delete and Lock User]

叼!!!

暖十777

#6 Created at ... [Delete] [Delete and Lock User]

还是没有搞清楚这段代码的运行顺序或流程,哪位能再仔细梳理一下,谢谢了

运行流程就是(个人理解,望指出不足):当调用Chain的一个属性(比如status)时,getattr函数中return了一个Chain的新的实例,且创建这个新的实例传入的参数是原来的path+path('status'),所以这时候新的实例的path是原来的_path+path('status'),这时候又是调用的新的实例的users,但是由于__getattr方法函数对于users有特殊的“照顾”,所以这时候返回的是一个函数,然后把users中的参数‘michael’传到lambda函数中,也就是x接收了‘michael’,然后这个函数返回的优势一个新的Chain的实例,创建这个新新的实例接收的参数是原来的_path+users+x('michael'),然后新新的实例的_path是原来的_path+users+x('michael')。

这个想法叼,执行到user('Michael')通过call自调用,将'Michael'传入迭代到self._path里

在此插入代码

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

fine519330933

#9 Created at ... [Delete] [Delete and Lock User]

不懂怎么得到具体的用户的API的流程,哪位可以解释一下吗

洛神ayone

#10 Created at ... [Delete] [Delete and Lock User]

Chain().status.user('Michael').time.sock

先执行Chain(),这是实例化一个Chain对象,此时的path为空‘’,接着Chian().status去获取这个对象的status属性,由于没有status属性,会自动执行getattr方法,这时会返回一个path为‘’+'/'+'status'的的Chain对象;接着Chain().status.user同上返回一个_path为'/status'+'/'+'user'【此时为/status/user】的Chain对象,接着直接调用这个对象实例本身,并且传入参数‘Michael’【假设这一步之前的对象是s,这一步执行的其实是s('Michael')】,直接调用这个对象实例本身会进入到对象的__call方法,这个方法返回的是一个_path为‘/status/user’+‘/’+'Michael'的Chain对象,这个对象再去获取time属性,返回_path为‘status/user/Michale/time’的Chain对象,接着这个对象再获取sock属性,返回_path为‘status/user/Michale/time/sock’的Chain对象


  • 1

Reply