我有一个向远程API发出请求的类。我希望能够减少我正在拨打的电话数量。我的类中的一些方法进行相同的API调用(但由于不同的原因),所以我想它们能够“共享”缓存的API响应。
我不完全确定使用可选参数或使用多个方法是否更Pythonic,因为这些方法在进行API调用时会有一些必需的参数。
以下是我看到的approches,您认为最好吗?
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
答案 0 :(得分: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的用户可以灵活地决定是否需要缓存功能。
答案 1 :(得分:2)
class B
中使用的方法没有任何问题。为了让您一眼就能看出实际上需要包含item_id
或cached_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
答案 2 :(得分:1)
最终,这是必须针对每种情况作出的判断。我会问自己,这两者中哪一个更贴合:
如果第一个最接近,请使用单独的方法。如果第二个是最接近的,请使用可选参数。您甚至可以通过测试参数的类型来实现单个方法,以避免传递其他参数。
答案 3 :(得分: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装饰器。
答案 4 :(得分:0)
缓存的响应应该保存在实例中,而不是像一袋Skittles一样传递 - 如果丢弃它会怎么样?
每个实例是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
方法。