[Python-es] ¿ Injección de código con decoradores o herencia ?

Hernán Foffani hfoffani en gmail.com
Mie Oct 23 16:20:21 CEST 2013


> Hernan M. F
> Particularmente encuentro el ejemplo que presentas como poco elegante. Porque en ese caso la clase C tiene una dependencia fija en el código al método F() de la clase D la cual no puede ser modificada (bueno, si puede ser modificada empleando settatr(), lo que hace a Python excelente desde mi punto de vista). La cuestión es que se programa una vez, pero se modifica el código y se revisa múltiples veces y en este caso el usuario de C tiene que leer todo el código para darse cuenta de que cambiar si necesita reemplazar D.F por otra cosa. Y finalmente si F es una función que no dependa de que argumentos reciba D en el constructor, entonces no es necesario que sea miembro de la clase D. Yo pondría tu ejemplo de la siguiente forma:
> 
> class C:
>  def __init__(self, f=None):
>    if f is None:
>      d = D()
>      f = d.f
> 
>    settatr(self, 'f', f)
> 
>  def f(self):
>    raise NotImplementedError()
> 

La dependencia la puedes quitar de forma similar a lo has puesto (inicializa con None, condicionalmente,
o como creas mas conveniente). Flexibiliza la firma usando kwargs, etc...

class C (B):
    def __init__(self, f_provider = D()):
       self._f_provider = f_provider
    def F(self):
       self._f_provider.F()

Mi respuesta anterior era prevenir el abuso de setattr cuando hay patrones de uso que son mucho
mas sencillos y prácticos.

Piensa en el caso de uso. Si inyectas vía setattr te impide que puedas ajustar luego el comportamiento.
Lo que se suele  necesitar en los casos reales es hacer algo antes y/o después de la invocación al
suministrador del servicio. Encima si inyectas F a B o C así, ¿cómo accede F a datos internos de B?

    …
    def F(self):
        hacer_algo_previo_con_self()
        self._f_provider.F( o_invocar_F_con_alguna_opción_especifica )
        hacer_otra_cosa_con_self_con_el_resultado_de_F()


> Esta variante le deja claro al usuario que solo con parametrizar la clase C puede reemplazar la maquinaria externa que consume la clase. Finalmente quiero decir (mi criterio nuevamente y el de algunas personas que asi lo ponen en sus blogs) que la herencia multiple no es la forma de inyectar comportamiento en una clase y que debe ser usada con mucho cuidado. Herencia es herencia, una persona hereda de animal pero un navegador web no hereda de conexión de red solo porque la use y la herencia lo pueda hacer parecer que funciona.

Es la discusión de décadas y hay opiniones de todo tipo. No voy a entrar en ésta, a no ser que hablemos de casos concreto
de código, no tengo intención definir qué es ser.

Como dije en mi mensaje anterior lo formal en Python es usar abc. (Ver ejemplo de Chema en un mail posterior).




Más información sobre la lista de distribución Python-es