2011-08-22 65 views
10

我有一个向远程API发出请求的类。我希望能够减少我正在拨打的电话数量。我班的一些方法会进行相同的API调用(但出于不同的原因),所以我想他们能够“共享”一个缓存的API响应。Python - 最好有多种方法或大量的可选参数?

我不完全确定是否Pythonic使用可选参数或使用多个方法,因为如果方法在进行API调用时具有一些必需的参数。

下面是我看到他们的认同,你认为哪个最好?

class A: 

    def a_method(item_id, cached_item_api_response = None): 
    """ Seems awkward having to supplied item_id even 
     if cached_item_api_response is given 
    """ 
    api_response = None 
    if cached_item_api_response: 
     api_response = cached_item_api_response 
    else: 
     api_response = ... # make api call using item_id 

    ... #do stuff 

或者这样:

class B: 

    def a_method(item_id = None, cached_api_response = None): 
    """ Seems awkward as it makes no sense NOT to supply EITHER 
     item_id or cached_api_response 
    """ 
    api_response = None 
    if cached_item_api_response: 
     api_response = cached_item_api_response 
    elif item_id: 
     api_response = ... # make api call using item_id 
    else: 
     #ERROR 

    ... #do stuff 

或者,这是比较合适的?

class C: 
    """Seems even more awkward to have different method calls""" 

    def a_method(item_id): 
     api_response = ... # make api call using item_id 
     api_response_logic(api_response) 

    def b_method(cached_api_response): 
     api_response_logic(cached_api_response) 

    def api_response_logic(api_response): 
     ... # do stuff 

回答

5

通常写入方法时人们可能认为的方法/对象应该做的一件事它应该做得很好。如果你的方法越来越多的参数需要越来越多的ifs代码,这可能意味着你的代码做了更多的事情。特别是如果这些参数触发完全不同的行为相反,也许可以通过拥有不同的类和使用重载方法来产生相同的行为。

也许你可以使用类似:

class BaseClass(object): 
    def a_method(self, item_id): 
     response = lookup_response(item_id) 
     return response 

class CachingClass(BaseClass): 
    def a_method(self, item_id): 
     if item_id in cache: 
      return item_from_cache 
     return super(CachingClass, self).a_method(item_id) 

    def uncached_method(self, item_id) 
     return super(CachingClass, self).a_method(item_id) 

这样,你可以拆分如何查找响应和缓存,同时,也使其灵活的API的用户决定他们想要的逻辑缓存能力与否。

+0

感谢您的编辑,但通常我会建议使用代表而不是继承,因为它使类更灵活。 – Rickard

+0

如果这不是你的意图,对不起改变它。这仅仅是一个Python问题,你的代码不是Python,所以我尽我所能猜测 - 随时回滚或更改它以反映你的意图。 (好的答案,已经得到了我的+1) – agf

2

在您的class B中使用的方法没有任何问题。为了使它更加明显,在你确实需要包括任何一眼item_idcached_api_response,我会把错误首先检查:

class B: 

    def a_method(item_id = None, cached_api_response = None): 
     """Requires either item_id or cached_api_response""" 

     if not ((item_id == None)^(cached_api_response == None)): 
      #error 

     # or, if you want to allow both, 
     if (item_id == None) and (cached_api_response == None): 
      # error 

     # you don't actually have to do this on one line 
     # also don't use it if cached_item_api_response can evaluate to 'False' 
     api_response = cached_item_api_response or # make api call using item_id 

     ... #do stuff 
1

最终,这是必须针对每种情况做出的判断。我会问自己,这两个关系更为密切配合:

  1. 两个完全不同的算法或行为,具有完全不同的语义,即使他们可以传递类似的信息
  2. 一个单一的概念性的想法,用一致的语义但基于输入的细微差别

如果第一个最接近,则采用单独的方法。如果第二个最接近,则使用可选参数。您甚至可以通过测试参数的类型来实现单个方法,以避免传递其他参数。

1

这是一个OO反模式。

class API_Connection(object): 
    def do_something_with_api_response(self, response): 
     ... 

    def do_something_else_with_api_response(self, response): 
     ... 

你对实例有两种方法,你明确地在它们之间传递状态?为什么这些方法并不是模块中的裸函数?

相反,考虑使用封装来帮助您通过让类的实例拥有api响应。

例如:

class API_Connection(object): 
    def __init__(self, api_url): 
     self._url = api_url 
     self.cached_response = None 

    @property 
    def response(self): 
     """Actually use the _url and get the response when needed.""" 
     if self._cached_response is None: 
      # actually calculate self._cached_response by making our 
      # remote call, etc 
      self._cached_response = self._get_api_response(self._url) 
     return self._cached_response 

    def _get_api_response(self, api_param1, ...): 
     """Make the request and return the api's response""" 

    def do_something_with_api_response(self): 
     # just use self.response 
     do_something(self.response) 

    def do_something_else_with_api_response(self): 
     # just use self.response 
     do_something_else(self.response) 

你有缓存和需要可以以任意顺序未做多个API请求,因为需要self.response第一种方法会计算它和所有其他将使用运行此响应的任何方法缓存值。希望很容易想象用多个URL或RPC调用来扩展它。如果你需要很多缓存上述response返回值的方法,那么你应该看看你的方法的memoization装饰器。

+0

我喜欢整体概念,但是如果参数在不同的调用之间有所不同,我会发现从'do_something'方法到'_get_api_response'方法有些困难。尽管如此,仍然值得+1。 –

0

缓存的响应应该保存在实例中,而不是像一袋吃喝玩乐一样传递 - 如果丢弃它,该怎么办?

item_id每个实例是唯一的,还是一个实例可以查询多个?如果能有一个以上的,我会像这样的东西去:

class A(object): 

    def __init__(self): 
     self._cache = dict() 

    def a_method(item_id): 
     """Gets api_reponse from cache (cache may have to get a current response). 
     """ 
     api_response = self._get_cached_response(item_id) 
     ... #do stuff 

    def b_method(item_id): 
     """'nother method (just for show) 
     """ 
     api_response = self._get_cached_response(item_id) 
     ... #do other stuff 

    def _get_cached_response(self, item_id): 
     if item_id in self._cache: 
      return self._cache[ item_id ] 
     response = self._cache[ item_id ] = api_call(item_id, ...) 
     return response 

    def refresh_response(item_id): 
     if item_id in self._cache: 
      del self._cache[ item_id ] 
     self._get_cached_response(item_id) 

如果您可能需要获得有关item_id的最新信息,你可以有一个refresh_response方法。

相关问题