2011-08-22 15 views
10

Tengo una clase que realiza solicitudes a una API remota. Me gustaría poder reducir el número de llamadas que estoy haciendo. Algunos de los métodos en mi clase hacen las mismas llamadas a la API (pero por diferentes razones), por lo que me gustaría tener la capacidad de 'compartir' una respuesta de la API en caché.Python: ¿es mejor tener múltiples métodos o muchos parámetros opcionales?

No estoy del todo seguro de si es más pitónico utilizar los parámetros opcionales o utilizar varios métodos, ya que los métodos tienen algunos parámetros necesarios si realizan una llamada a la API.

Aquí están los enfoques como yo los veo, ¿cuál crees que es el mejor?

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 

O esto:

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 

O se trata más apropiado?

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 

Respuesta

5

Normalmente cuando se escribe método se podría argumentar que un método/objeto debe hacer una cosa y debería hacerlo bien. Si su método obtiene más y más parámetros que requieren cada vez más ifs en su código, probablemente signifique que su código está haciendo más de una cosa. Especialmente si esos parámetros disparan un comportamiento totalmente diferente. En cambio, tal vez el mismo comportamiento podría producirse teniendo diferentes clases y teniendo métodos de sobrecarga.

Tal vez usted podría utilizar algo como:

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) 

De esta manera se puede dividir la lógica de cómo buscar la respuesta y el almacenamiento en caché al mismo tiempo por lo que es flexible para el usuario de la API para decidir si quieren las capacidades de almacenamiento en caché o no.

+0

Gracias por la edición, pero normalmente sugeriría utilizar la delegación en lugar de la herencia, ya que hace que las clases sean más flexibles. – Rickard

+0

Si esta no era su intención, lo siento por cambiarla. Era solo una pregunta de Python y tu código no era Python, así que hice mi mejor conjetura; siéntete libre de revertirlo o cambiarlo para reflejar tu intención. (Buena respuesta de cualquier manera, ya obtuve mi +1) – agf

2

No hay nada malo con el método utilizado en su class B. Para que sea más evidente a simple vista que en realidad se necesita incluir ya sea item_id o cached_api_response, me pondría a la comprobación de errores en primer lugar:

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

En última instancia, este es un juicio que debe hacerse para cada situación. Me preguntaba a mí mismo, ¿cuál de estos dos más estrechamente encaja:

  1. Dos algoritmos o acciones completamente diferentes, completamente diferentes semántica, a pesar de que pueden pasar información similar
  2. Una idea conceptual única, con la semántica consistentes , pero con matices basados ​​en la entrada

Si el primero es el más cercano, vaya con métodos separados. Si el segundo es el más cercano, ve con argumentos opcionales. Incluso podría implementar un único método probando el tipo de argumento (s) para evitar pasar argumentos adicionales.

1

Este es un anti-patrón OO.

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

    def do_something_else_with_api_response(self, response): 
     ... 

Existen dos métodos en una instancia y estás pasando estado entre ellos de forma explícita? ¿Por qué estos métodos y no son funciones simples en un módulo?

En su lugar, piense en utilizar la encapsulación para ayudarlo haciendo que la instancia de la clase sea propietaria de la respuesta de la API.

Por ejemplo:

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) 

Usted tiene el almacenamiento en caché y cualquier método que necesita esta respuesta se puede ejecutar en cualquier orden sin hacer múltiples solicitudes de API debido a que el primer método que necesita self.response lo calculará y todos los demás utilizará el valor en caché Es de esperar que sea fácil imaginar extender esto con múltiples URL o llamadas RPC. Si necesita muchos métodos que guarden en caché los valores de retorno, como response, debe buscar en un decorador de memoria sus métodos.

+0

Me gusta el concepto general, pero veo algunas dificultades para pasar de los métodos 'do_something' al método' _get_api_response' si hay parámetros que difieren entre las llamadas. Todavía vale la pena un +1, sin embargo. –

0

La respuesta almacenada en caché se debe guardar en la instancia, no se debe pasar como una bolsa de bolos. ¿Qué pasaría si la dejara caer?

¿Es item_id único por instancia, o puede una instancia realizar consultas para más de uno? Si se puede tener más de una, me gustaría ir con algo como esto:

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) 

Y si es posible que tenga que obtener la información más actualizada sobre item_id, puede tener un método refresh_response.

Cuestiones relacionadas