Python:如何使用类魔术启用以下API

时间:2012-01-31 08:19:35

标签: python api api-design

目标:

给出类似的东西:

stackoverflow.users['55562'].questions.unanswered()

我希望将其转换为以下内容:

http://api.stackoverflow.com/1.1/users/55562/questions/unanswered

我已经能够实现这一点,使用以下类:

class SO(object):

    def __init__(self,**kwargs):
        self.base_url = kwargs.pop('base_url',[]) or 'http://api.stackoverflow.com/1.1'
        self.uriparts = kwargs.pop('uriparts',[])
        for k,v in kwargs.items():
            setattr(self,k,v)

    def __getattr__(self,key):
        self.uriparts.append(key)
        return self.__class__(**self.__dict__)        

    def __getitem__(self,key):
        return self.__getattr__(key)

    def __call__(self,**kwargs):
        return "%s/%s"%(self.base_url,"/".join(self.uriparts))

if __name__ == '__main__':
    print SO().abc.mno.ghi.jkl()
    print SO().abc.mno['ghi'].jkl()

#prints the following
http://api.stackoverflow.com/1.1/abc/mno/ghi/jkl
http://api.stackoverflow.com/1.1/abc/mno/ghi/jkl

现在我的问题是我做不了类似的事情:

stackoverflow = SO()
user1 = stackoverflow.users['55562']
user2 = stackoverflow.users['55462']
print user1.questions.unanswered
print user2.questions.unanswered

#prints the following
http://api.stackoverflow.com/1.1/users/55562/users/55462/questions/unanswered
http://api.stackoverflow.com/1.1/users/55562/users/55462/questions/unanswered/questions/unanswered

基本上,user1和user2引用相同的SO对象,因此它不能代表不同的用户。

我一直在想任何指针都会有所帮助,因为这种额外的功能会使API变得更有趣。

2 个答案:

答案 0 :(得分:2)

恕我直言,当你重新创建一个新的stackoverflow对象时,你需要将参数与旧实例属性的深度副本分开

import copy
........    
def __getattr__(self,key):
    dict = copy.deepcopy(self.__dict__)
    dict['uriparts'].append(key)
    return self.__class__(**dict)
....

如果您希望URI部分具有更大的灵活性,则需要抽象来实现更清洁的设计。例如:

class SOURIParts(object):
    def __init__(self, so, uriparts, **kwargs):
        self.so = so
        self.uriparts = uriparts
        for k,v in kwargs.items():
            setattr(self,k,v)

    def __getattr__(self,key):
        return SOURIParts(self.so, self.uriparts+[key])

    def __getitem__(self,key):
        return self.__getattr__(key)

    def __call__(self,**kwargs):
        return "%s/%s"%(self.so.base_url,"/".join(self.uriparts))

class SO(object):
    def __init__(self, base_url='http://api.stackoverflow.com/1.1'):
        self.base_url =  base_url

    def __getattr__(self,key):
        return SOURIParts(self, [])

    def __getitem__(self,key):
        return self.__getattr__(key)

我希望这会有所帮助。

答案 1 :(得分:0)

您可以覆盖__getslice__(Python 2.7)或getitem()(Python3.x)并使用记忆装饰器,这样如果您请求的切片(用户ID)已被查找,它将会使用缓存结果 - 否则它可以检索结果并填充现有的SO实例对象。

但是,我认为解决问题的更多OO方法是使SO成为一个纯查找模块,它返回堆栈溢出用户对象,然后对配置文件详细信息进行更深入的挖掘查找。但那就是我。