2012-06-13 22 views
15

Estoy creando una clase de generador de consultas que ayudará a construir una consulta para mongodb a partir de parámetros de URL. Nunca he hecho mucha programación orientada a objetos, o clases diseñadas para consumo por personas que no sean yo mismo, además de usar construcciones de lenguaje básico y el uso de modelos integrados de django.¿Deben devolverse los métodos de clase internos o simplemente modificar las variables de instancia en python?

Así que tienen esta clase QueryBuilder

class QueryHelper(): 
    """ 
    Help abstract out the problem of querying over vastly 
    different dataschemas. 
    """ 

    def __init__(self, collection_name, field_name, params_dict): 
     self.query_dict = {} 
     self.params_dict = params_dict 
     db = connection.get_db() 
     self.collection = db[collection_name] 

    def _build_query(self): 
     # check params dict and build a mongo query 
     pass 

Ahora en _build_query voy a estar revisando el dict params y llenar query_dict de manera que pase a Mongo find() función. Al hacer esto, me preguntaba si había un enfoque absolutamente correcto para si _build_query debería devolver un diccionario o si solo debería modificar self.query_dict. Como es un método interno, supongo que está bien simplemente modificar self.query_dict. ¿Hay una manera correcta (pythonic) de acercarse a esto? ¿Es esto tonto y no una decisión de diseño importante? Cualquier ayuda es apreciada. Gracias.

Respuesta

4

La devolución de un valor es preferible, ya que le permite mantener todas las modificaciones de atributos en un solo lugar (__init__). Además, esto hace que sea más fácil extender el código más tarde; supongamos que desea anular _build_query en una subclase, el método de anulación puede simplemente devolver un valor, sin necesidad de saber qué atributo establecer. He aquí un ejemplo:

class QueryHelper(object): 
    def __init__(self, param, text): 
     self._param = param 
     self._query = self._build_query(text) 

    def _build_query(self, text): 
     return text + " and ham!" 

class RefinedQueryHelper(QueryHelper): 
    def _build_query(self, text): 
     # no need to know how the query object is going to be used 
     q = super(RefinedQueryHelper, self)._build_query() 
     return q.replace("ham", "spam") 

frente a la "versión colocador":

class QueryHelper(object): 
    def __init__(self, param, text): 
     self._param = param 
     self._build_query(text) 

    def _build_query(self, text): 
     self._query = text + " and ham!" 

class RefinedQueryHelper(QueryHelper): 
    def _build_query(self, text): 
     # what if we want to store the query in __query instead? 
     # then we need to modify two classes... 
     super(RefinedQueryHelper, self)._build_query() 
     self._query = self._query.replace("ham", "spam") 

Si decide configurar un atributo, es posible que desee llamar al método _set_query para mayor claridad.

9

Está perfectamente bien modificar self.query_dict ya que la idea de la programación orientada a objetos es que los métodos pueden modificar el estado de un objeto. Mientras un objeto esté en un estado consistente después de que un método haya terminado, estás bien. El hecho de que _build_query sea un método interno no importa. Puede elegir llamar al _build_query después en __init__ para construir la consulta ya cuando se crea el objeto.

La decisión es principalmente importante para fines de prueba. Para fines de prueba de pieles, es conveniente probar cada método individualmente sin tener que necesariamente probar el estado del objeto completo. Pero eso no se aplica en este caso porque estamos hablando de un método interno, de modo que usted solo decide cuándo llamar a ese método, no a otros objetos u otro código.

6

Si devuelve algo, sugeriría self. Volviendo self de métodos de instancia es conveniente para el encadenamiento de método, ya que cada valor de retorno permite que otro método de llamada en el mismo objeto:

foo.add_thing(x).add_thing(y).set_goal(42).execute() 

Esto se refiere a veces como una API "fluidez".

Sin embargo, mientras Python permite método de encadenamiento para tipos inmutables tales como int y str, que no proporciona por métodos de contenedores mutables como list y set diseño-so -por podría decirse que no es "Pythonic" para hacerlo para su propio tipo mutable. Aún así, muchas bibliotecas de Python tienen API "fluidas".

Un inconveniente es que una API así puede dificultar la depuración.Dado que ejecuta la declaración completa o nada de eso, no puede ver fácilmente el objeto en puntos intermedios dentro de la declaración. Por supuesto, generalmente encuentro print perfectamente adecuado para depurar el código de Python, así que simplemente arrojaría un print en cualquier método cuyo valor de retorno me interesara.

+1

'.execute()' no es necesario. Puede disparar la ejecución usando '.all()', '.first()', '.one()'. – jfs

+0

Claro, eso es bueno si tienes más de un método que realmente hace algo. – kindall

2

Si bien los métodos de un objeto modifican directamente su estado, a veces puede ser ventajoso que un objeto sea su propio "cliente" y acceda indirectamente a través de (típicamente) métodos de acceso privados.

Esto proporciona una mejor encapsulación y los beneficios que se derivan de eso. Sin embargo, hacerlo puede ser poco práctico, ya que requeriría demasiado código adicional y, a menudo, es más lento, lo que podría afectar el rendimiento de una cantidad inaceptable de forma adversa, por lo que hay que hacer concesiones con el ideal.

Cuestiones relacionadas