resueltos poo polimorfismo otra objetos multiple metodos metodo llamar lista importar herencia ejercicios clases clase python oop instance-variables

poo - polimorfismo python



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

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 programació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 existí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.


Está perfectamente bien modificar self.query_dict ya que la idea general de la programació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 finalizado, 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 usted solo decida cuándo llamar a ese método, no a otros objetos u otro código.


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 acceder a ellos mismos de manera indirecta a través de métodos de acceso privados (típicamente).

Hacer esto proporciona una mejor encapsulation 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 demasiado código adicional y, a menudo, es más lento, lo que podría afectar negativamente al rendimiento, por lo que las compensaciones deben hacerse con el ideal.


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, mientras Python permite el encadenamiento de métodos para tipos inmutables como int y str , no lo proporciona para los 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 declaración completa o ninguna de ellas, no puede ver fácilmente el objeto en puntos intermedios dentro de la declaración. Por supuesto, generalmente encuentro que la print perfectamente adecuada para depurar el código Python, ¡así que simplemente lanzo una print en cualquier método cuyo valor de devolución me interesara!