KEMBAR78
Python client api | PDF
茴香豆的茴有几种写法
 记Python的⼀一个客户端实现
     dreampuf Jul, 2012
本地调用


                main




       printf          scanf
远程调用


                  Client

             Protocol Stream

       ServerA             ServerB
Esty-python
   class MockAPI(API):
       api_url = 'http://host'
       api_version = 'v1'
       def etsy_home(self):
           return Test.scratch_dir
       def get_method_table(self, *args):
           return [{'name': 'testMethod',
                    'uri': '/test/{test_id}',
                    'http_method': 'GET',
                    'params': {
                        'limit': 'int',
                        'test_id': 'user_id_or_name',
                        'offset': 'int',
                        'fizz': 'enum(foo, bar, baz)',
                        'buzz': 'float',
                        'blah': 'unknown type',
                        'kind': 'string',
                        },
                    'type': 'int',
                    'description': 'test method.'}]
       def _get_url(self, url, http_method, content_type, body):
           return '{ "count": 1, "results": [3] }'
Esty-python
   class MockAPI(API):
       api_url = 'http://host'
       api_version = 'v1'
       def etsy_home(self):
           return Test.scratch_dir
       def get_method_table(self, *args):
           return [{'name': 'testMethod',
                    'uri': '/test/{test_id}',
                    'http_method': 'GET',                     重用
      不直观           'params': {
                        'limit': 'int',                       不依赖URL
缺乏语法检查                  'test_id': 'user_id_or_name',
                        'offset': 'int',                      类型验证
    不易调试                'fizz': 'enum(foo, bar, baz)',
                        'buzz': 'float',
                        'blah': 'unknown type',
                                                              URL拓展
                        'kind': 'string',
                        },
                    'type': 'int',
                    'description': 'test method.'}]
       def _get_url(self, url, http_method, content_type, body):
           return '{ "count": 1, "results": [3] }'
GuokrAPI v1


          class GuokrAPI(API):
              HOST = "http://localhost/"
              METHODS = [{
                   "name": "get_tag",
                   "url": "tags",
                   "method": "GET",
                   "description": "Get the tag instance list",
              }, {
                   "name": "get_tags",
                   "url": "tags/%(tagname)s",
                   "method": "GET",
                   "description": "Get a tag instance",
              }]
GuokrAPI v1


          class GuokrAPI(API):
              HOST = "http://localhost/"
              METHODS = [{
    不直观            "name": "get_tag",
                   "url": "tags",
                                                                 重用
                   "method": "GET",
缺乏语法检查             "description": "Get the tag instance list",   不依赖URL
              }, {
   不易调试            "name": "get_tags",                           URLLIB3
                   "url": "tags/%(tagname)s",
   类型验证            "method": "GET",                              URL拓展
                   "description": "Get a tag instance",
              }]
GuokrAPI v2
   tagging = guokr.type(                import guokr
       'tagging'                        import minerva.types as minerva
   ).fields({
       'id': 'int',                     tags = guokr.resources([
       'tag': 'string',                     minerva.tag
       'taggable': 'taggable',          ])
       'tag': 'string',
       'user': 'ukey',                  taggings = guokr.resources([
       'date_create': 'datetime',           minerva.tag,
       'date_deleted': 'datetime',          minerva.taggable,
   }).keys([                            ]).namespace(
     'id'                                   'taggings'
   ]).end()                             )


                 mercury.group.mixin(minerva.taggable)

                 # POST on url /tags
                 call = resources.tags.create().format('jsonp')

                 # POST on url /taggings/tags/科学/group/654321
                 call = resources.taggings.update({
                     'tag': '科学',
                     'taggable': group,
                     'user': 'afsrgx',
                 })
GuokrAPI v2
   tagging = guokr.type(                import guokr
       'tagging'                        import minerva.types as minerva
   ).fields({
       'id': 'int',                     tags = guokr.resources([
       'tag': 'string',                     minerva.tag
       'taggable': 'taggable',          ])
       'tag': 'string',
       'user': 'ukey',                  taggings = guokr.resources([
       'date_create': 'datetime',           minerva.tag,
       'date_deleted': 'datetime',          minerva.taggable,
   }).keys([                            ]).namespace(
     'id'                                   'taggings'
   ]).end()                             )


                 mercury.group.mixin(minerva.taggable)
                                                                   重用
                 # POST on url /tags
  URL拓展          call = resources.tags.create().format('jsonp')
                                                                   类型验证
  抽象繁多           # POST on url /taggings/tags/科学/group/654321      链式语法
                 call = resources.taggings.update({
                     'tag': '科学',                                  清晰
                     'taggable': group,
                     'user': 'afsrgx',
                 })
GuokrAPI v2
GuokrAPI v2




              Pyt hon
GuokrAPI v2




              Py   t hon   ic
GuokrAPI v3

  TUPLE_TAGGINGABLE = ("article", "post", "blog", "group", "question")
  class GuokrAPI(API):
      HOST = "http://localhost:5000/"

      @GET("tags/%(tag)s")
      def get_tag(tag): pass
      @GET("tags/%(tag)s/feeds")
      def get_tag_feeds(tag, filter=str): pass
      @POST("tags/")
      def add_tag(tag, synonym=str, logo=str, description=str): pass
      @POST("tag/%(tag)s")
      def post_tag(tag, logo=str, description=str): pass

   class GuokrCMSAPI(GuokrAPI):
      @GET("cms/tags/filter:%(prefix)s")
      def get_tag_prefix(prefix): pass
      #Duplicate
      #@POST("cms/tags")
      #def add_tag(self, tag, synonym=None, logo=None, description=None): pass
      @POST("cms/tags/%(tag)s")
      def post_tag(tag, logon=str, synonyms=str): pass #synonyms VS synonym
      @POST("cms/tags/%(tag)s/lock")
GuokrAPI v3

  TUPLE_TAGGINGABLE = ("article", "post", "blog", "group", "question")
  class GuokrAPI(API):
      HOST = "http://localhost:5000/"

      @GET("tags/%(tag)s")                                               重用
      def get_tag(tag): pass
      @GET("tags/%(tag)s/feeds")                                         类型验证
      def get_tag_feeds(tag, filter=str): pass
      @POST("tags/")                                                     清晰
      def add_tag(tag, synonym=str, logo=str, description=str): pass
      @POST("tag/%(tag)s")                                               调试/直观
      def post_tag(tag, logo=str, description=str): pass

   class GuokrCMSAPI(GuokrAPI):
      @GET("cms/tags/filter:%(prefix)s")
      def get_tag_prefix(prefix): pass
      #Duplicate
      #@POST("cms/tags")
      #def add_tag(self, tag, synonym=None, logo=None, description=None): pass
      @POST("cms/tags/%(tag)s")
      def post_tag(tag, logon=str, synonyms=str): pass #synonyms VS synonym
      @POST("cms/tags/%(tag)s/lock")
结论

•统⼀一   随时注意前后接口⼀一致
•抽象    隐藏不必要的细节
•充要    只接受充分且必须的参数
•直观    对人友好,对机器友好(调试)
•原生    Pythonic
DATA ENTRY
Data Entry v1




                a = (1, 2, 3)
Data Entry v2




                a = {“a”:1,
                     “b”:2,
                     “c”:3)
Data Entry v3




class uDict(dict):
    def __getattr__(self, name):
        return self.__getitem__(name)
Data Entry v4


class uDict(dict):
    def __getattr__(self, name, default=None):
        try:
             return self.__getitem__(name)
        except KeyError:
             return default

a = uDict(a=1, b=2, c=3)
print a.b
print a.g
Data Entry v5




       class Entry(object):
           def __init__(self, a, b, c):
               self.a = a
               self.b = b
               self.c = c
Data Entry v6




     from collections import namedtuple
     Entry = namedtuple("Entry", "a b c")
     a = Entry(a=1, b=2, c=3)
Data Entry v7




class Entry(namedtuple("Entry", "a b c")):
    def __new__(cls, a=None, b=None, c=None):
        return super(Entry, cls).__new__(cls, a, b, c)
谢谢!
REFERENCE
•Etsy-python
https://github.com/mcfunley/etsy-python
•Lambda Picture
http://www.flickr.com/photos/rofi/2097239111/

Python client api

  • 1.
  • 3.
    本地调用 main printf scanf
  • 4.
    远程调用 Client Protocol Stream ServerA ServerB
  • 5.
    Esty-python class MockAPI(API): api_url = 'http://host' api_version = 'v1' def etsy_home(self): return Test.scratch_dir def get_method_table(self, *args): return [{'name': 'testMethod', 'uri': '/test/{test_id}', 'http_method': 'GET', 'params': { 'limit': 'int', 'test_id': 'user_id_or_name', 'offset': 'int', 'fizz': 'enum(foo, bar, baz)', 'buzz': 'float', 'blah': 'unknown type', 'kind': 'string', }, 'type': 'int', 'description': 'test method.'}] def _get_url(self, url, http_method, content_type, body): return '{ "count": 1, "results": [3] }'
  • 6.
    Esty-python class MockAPI(API): api_url = 'http://host' api_version = 'v1' def etsy_home(self): return Test.scratch_dir def get_method_table(self, *args): return [{'name': 'testMethod', 'uri': '/test/{test_id}', 'http_method': 'GET', 重用 不直观 'params': { 'limit': 'int', 不依赖URL 缺乏语法检查 'test_id': 'user_id_or_name', 'offset': 'int', 类型验证 不易调试 'fizz': 'enum(foo, bar, baz)', 'buzz': 'float', 'blah': 'unknown type', URL拓展 'kind': 'string', }, 'type': 'int', 'description': 'test method.'}] def _get_url(self, url, http_method, content_type, body): return '{ "count": 1, "results": [3] }'
  • 7.
    GuokrAPI v1 class GuokrAPI(API): HOST = "http://localhost/" METHODS = [{ "name": "get_tag", "url": "tags", "method": "GET", "description": "Get the tag instance list", }, { "name": "get_tags", "url": "tags/%(tagname)s", "method": "GET", "description": "Get a tag instance", }]
  • 8.
    GuokrAPI v1 class GuokrAPI(API): HOST = "http://localhost/" METHODS = [{ 不直观 "name": "get_tag", "url": "tags", 重用 "method": "GET", 缺乏语法检查 "description": "Get the tag instance list", 不依赖URL }, { 不易调试 "name": "get_tags", URLLIB3 "url": "tags/%(tagname)s", 类型验证 "method": "GET", URL拓展 "description": "Get a tag instance", }]
  • 9.
    GuokrAPI v2 tagging = guokr.type( import guokr     'tagging' import minerva.types as minerva ).fields({     'id': 'int', tags = guokr.resources([     'tag': 'string',     minerva.tag     'taggable': 'taggable', ])     'tag': 'string',     'user': 'ukey', taggings = guokr.resources([     'date_create': 'datetime',     minerva.tag,     'date_deleted': 'datetime',     minerva.taggable, }).keys([ ]).namespace(   'id'     'taggings' ]).end() ) mercury.group.mixin(minerva.taggable) # POST on url /tags call = resources.tags.create().format('jsonp') # POST on url /taggings/tags/科学/group/654321 call = resources.taggings.update({     'tag': '科学',     'taggable': group,     'user': 'afsrgx', })
  • 10.
    GuokrAPI v2 tagging = guokr.type( import guokr     'tagging' import minerva.types as minerva ).fields({     'id': 'int', tags = guokr.resources([     'tag': 'string',     minerva.tag     'taggable': 'taggable', ])     'tag': 'string',     'user': 'ukey', taggings = guokr.resources([     'date_create': 'datetime',     minerva.tag,     'date_deleted': 'datetime',     minerva.taggable, }).keys([ ]).namespace(   'id'     'taggings' ]).end() ) mercury.group.mixin(minerva.taggable) 重用 # POST on url /tags URL拓展 call = resources.tags.create().format('jsonp') 类型验证 抽象繁多 # POST on url /taggings/tags/科学/group/654321 链式语法 call = resources.taggings.update({     'tag': '科学', 清晰     'taggable': group,     'user': 'afsrgx', })
  • 11.
  • 12.
    GuokrAPI v2 Pyt hon
  • 13.
    GuokrAPI v2 Py t hon ic
  • 14.
    GuokrAPI v3 TUPLE_TAGGINGABLE = ("article", "post", "blog", "group", "question") class GuokrAPI(API): HOST = "http://localhost:5000/" @GET("tags/%(tag)s") def get_tag(tag): pass @GET("tags/%(tag)s/feeds") def get_tag_feeds(tag, filter=str): pass @POST("tags/") def add_tag(tag, synonym=str, logo=str, description=str): pass @POST("tag/%(tag)s") def post_tag(tag, logo=str, description=str): pass class GuokrCMSAPI(GuokrAPI): @GET("cms/tags/filter:%(prefix)s") def get_tag_prefix(prefix): pass #Duplicate #@POST("cms/tags") #def add_tag(self, tag, synonym=None, logo=None, description=None): pass @POST("cms/tags/%(tag)s") def post_tag(tag, logon=str, synonyms=str): pass #synonyms VS synonym @POST("cms/tags/%(tag)s/lock")
  • 15.
    GuokrAPI v3 TUPLE_TAGGINGABLE = ("article", "post", "blog", "group", "question") class GuokrAPI(API): HOST = "http://localhost:5000/" @GET("tags/%(tag)s") 重用 def get_tag(tag): pass @GET("tags/%(tag)s/feeds") 类型验证 def get_tag_feeds(tag, filter=str): pass @POST("tags/") 清晰 def add_tag(tag, synonym=str, logo=str, description=str): pass @POST("tag/%(tag)s") 调试/直观 def post_tag(tag, logo=str, description=str): pass class GuokrCMSAPI(GuokrAPI): @GET("cms/tags/filter:%(prefix)s") def get_tag_prefix(prefix): pass #Duplicate #@POST("cms/tags") #def add_tag(self, tag, synonym=None, logo=None, description=None): pass @POST("cms/tags/%(tag)s") def post_tag(tag, logon=str, synonyms=str): pass #synonyms VS synonym @POST("cms/tags/%(tag)s/lock")
  • 16.
    结论 •统⼀一 随时注意前后接口⼀一致 •抽象 隐藏不必要的细节 •充要 只接受充分且必须的参数 •直观 对人友好,对机器友好(调试) •原生 Pythonic
  • 17.
  • 18.
    Data Entry v1 a = (1, 2, 3)
  • 19.
    Data Entry v2 a = {“a”:1, “b”:2, “c”:3)
  • 20.
    Data Entry v3 classuDict(dict): def __getattr__(self, name): return self.__getitem__(name)
  • 21.
    Data Entry v4 classuDict(dict): def __getattr__(self, name, default=None): try: return self.__getitem__(name) except KeyError: return default a = uDict(a=1, b=2, c=3) print a.b print a.g
  • 22.
    Data Entry v5 class Entry(object): def __init__(self, a, b, c): self.a = a self.b = b self.c = c
  • 23.
    Data Entry v6 from collections import namedtuple Entry = namedtuple("Entry", "a b c") a = Entry(a=1, b=2, c=3)
  • 24.
    Data Entry v7 classEntry(namedtuple("Entry", "a b c")): def __new__(cls, a=None, b=None, c=None): return super(Entry, cls).__new__(cls, a, b, c)
  • 25.
  • 26.