拥有多个方法或大量可选参数更好吗?

问题描述 投票:0回答:5

我有一个向远程 API 发出请求的类。我希望能够减少拨打的电话数量。我的类中的一些方法进行相同的 API 调用(但出于不同的原因),因此我希望它们能够“共享”缓存的 API 响应。

我不完全确定使用可选参数或使用多个方法是否更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
python caching optional-parameters
5个回答
5
投票

通常在编写方法时,人们可能会认为方法/对象应该做一件事并且应该做得很好。如果您的方法获得越来越多的参数,而这些参数需要代码中越来越多的 if,则可能意味着您的代码正在做不止一件事。特别是如果这些参数触发完全不同的行为。相反,也许可以通过使用不同的类并让它们重载方法来产生相同的行为。

也许你可以使用类似的东西:

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 用户可以灵活地决定是否需要缓存功能。


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

1
投票

最终,这是必须针对每种情况做出的判断。我会问自己,这两者中哪一个更适合:

  1. 两种完全不同的算法或动作,具有完全不同的语义,即使它们可能传递相似的信息
  2. 单一的概念想法,具有一致的语义,但基于输入的细微差别

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


1
投票

这是面向对象的反模式。

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
,那么您应该为您的方法寻找一个记忆化装饰器。


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
方法。

© www.soinside.com 2019 - 2024. All rights reserved.