Python - Лучше иметь несколько методов или множество дополнительных параметров? - PullRequest
10 голосов
/ 22 августа 2011

У меня есть класс, который делает запросы к удаленному 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

Ответы [ 5 ]

5 голосов
/ 22 августа 2011

Обычно при написании метода можно утверждать, что метод / объект должен выполнять одну вещь, и он должен делать это хорошо. Если ваш метод получает все больше и больше параметров, которые требуют все больше и больше 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, чтобы решить, хотят ли они возможности кэширования или нет.

2 голосов
/ 22 августа 2011

Нет ничего плохого в методе, используемом в 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 голос
/ 22 августа 2011

Это анти-шаблон ОО.

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 выше, вам следует обратиться к декоратору памятки для ваших методов.

1 голос
/ 22 августа 2011

В конечном итоге это суждение, которое должно быть вынесено для каждой ситуации.Я хотел бы спросить себя, какой из этих двух более точно подходит:

  1. Два совершенно разных алгоритма или действия, с совершенно другой семантикой, даже если им может передаваться похожая информацияконцептуальная идея, с согласованной семантикой, но с нюансами, основанными на вводе

Если первый наиболее близок, используйте отдельные методы.Если второе ближе всего, используйте необязательные аргументы.Вы можете даже реализовать один метод, протестировав тип аргумента (ов), чтобы избежать передачи дополнительных аргументов.

0 голосов
/ 01 октября 2011

Кэшированный ответ должен быть сохранен в экземпляре, а не передаваться как мешок с кеглями - что, если вы уронили его?

Является ли 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.

...