¿Deberían los métodos de clase internos devolver valores o simplemente modificar las variables de instancia?

Estoy creando una clase de generador de consultas que ayudará en la construcción de una consulta para mongodb a partir de parámetros de URL. Nunca he hecho mucha progtwigción orientada a objetos, ni he diseñado clases para el consumo por parte de personas que no sean yo, además de usar construcciones de lenguaje básico y usar los modelos incorporados de django.

Así que tengo 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 , params_dict el params_dict y query_dict para pasarlo a la función find() mongo. Al hacer esto, me preguntaba si había un enfoque absolutamente correcto para determinar si _build_query debería devolver un diccionario o si solo debería modificar self.query_dict . Dado que es un método interno, asumiría que está bien modificar solo self.query_dict . ¿Hay una manera correcta (pythonic) de acercarse a esto? ¿Es esto una tontería y no es una decisión de diseño importante? Cualquier ayuda es apreciada.

Es preferible __init__ un valor 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 adelante; Supongamos que desea anular _build_query en una subclase, entonces el método de anulación solo puede devolver un valor, sin necesidad de saber qué atributo establecer. Aquí hay 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") 

vs. la “versión de setter”:

 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 elige establecer un atributo, es posible que desee llamar al método _set_query para mayor claridad.

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

La decisión en su mayoría importa para fines de prueba. Para propósitos de pruebas de piel, es conveniente probar cada método individualmente sin tener que probar necesariamente el estado de todo el objeto. Pero eso no se aplica en este caso porque estamos hablando de un método interno para que solo usted decida cuándo llamar a ese método, no a otros objetos u otro código.

Si devuelves algo en absoluto, te sugiero que lo hagas. Devolver self desde métodos de instancia es conveniente para el encadenamiento de métodos, ya que cada valor devuelto permite otra llamada de método en el mismo objeto:

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

Esto se conoce a veces como una API “fluida”.

Sin embargo, si bien Python permite el encadenamiento de métodos para tipos inmutables como int y str , no lo proporciona para métodos de contenedores mutables tales como list y set por diseño, por lo que posiblemente no sea “Pythonic” hacerlo por su propio mutable. tipo. Sin embargo, muchas bibliotecas de Python tienen API “fluidas”.

Un inconveniente es que tal API puede hacer que la depuración sea más difícil. Ya que ejecuta la statement completa o ninguna de ellas, no puede ver fácilmente el objeto en puntos intermedios dentro de la statement. Por supuesto, generalmente encuentro que la print adecuada para la depuración del código Python, ¡así que simplemente lanzo una print en cualquier método cuyo valor de retorno me interesara!

Si bien es común que los métodos de un objeto modifiquen directamente su estado, a veces puede ser ventajoso que un objeto sea su propio “cliente”, y que accedan a ellos mismos de manera indirecta a través de (típicamente) métodos de acceso privado. En Python, puede hacer esto fácilmente mediante la creación de la property() / función incorporada property() .

Hacer esto proporciona una mejor encapsulación y los beneficios que se derivan de ello (el principal es el aislamiento de los detalles de la implementación). Sin embargo, hacerlo puede ser poco práctico porque requeriría mucho código adicional y, a menudo, es más lento, lo que podría afectar adversamente al rendimiento en una cantidad inaceptable, por lo que a menudo es necesario hacer concesiones con respecto a este ideal.