Python: как включить следующий API с помощью класса magic - PullRequest
0 голосов
/ 31 января 2012

Цель:

Учитывая что-то вроде:

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 ]

2 голосов
/ 31 января 2012

ИМХО, когда вы воссоздаете новый объект 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)

Надеюсь, это поможет.

0 голосов
/ 31 января 2012

Вы можете переопределить __getslice__ (Python 2.7) или getitem() (Python3.x) и использовать декоратор запоминания, чтобы, если запрашиваемый вами фрагмент (идентификатор пользователя) уже был найден, он использовал кэшированные результаты -- иначе он мог бы получить результаты и заполнить существующий объект экземпляра SO.

Однако, я думаю, что более ОО-способ решения проблемы - сделать SO чистым поисковым модулем, который возвращает пользовательские объекты переполнения стека, которые затем будут иметьболее глубокие поиски для деталей профиля.Но это только я.

...