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

Chema Cortes pych3m4 en gmail.com
Mie Oct 23 11:56:45 CEST 2013


El día 22 de octubre de 2013 14:50, Yeiniel Suárez Sosa
<yeiniel en uclv.cu> escribió:

> 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()
>
> Esta variante le deja claro al usuario que solo con parametrizar la clase C
> puede reemplazar la maquinaria externa que consume la clase.

Para hacer estas cosas estarían las "clases abstractas". Aunque en
python no es habitual, existe ciertos esfuerzos para introducir clases
abstractas canalizados a través del módulo 'abc' (Abstract Base
Classes).

Un modo "recomendable" para hacer este tipo de clases sería:

import abc

class C:
    __metaclass__=abc.ABCMeta

    def run(self):
        self.f()

    @abc.abstractmethod
    def f(self):
        pass

Un ejemplo de su uso:

class D(C):

    def f(self):
       other_f()  #parcheo

Si el "consumidor" de la clase olvidase sobrecargar alguno de los
métodos abstractos de la clase, se produciría un error en tiempo de
ejecución.


>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.

A la hora de extender una librería, habría que distinguir entre
"especialización" (herencia) e "inyección" (parcheo). Normalmente, la
especialización se hace hace añadiendo una nueva clase al final de la
jerarquía, mientras que la inyección se puede hacer sobre cualquier
nodo de la jerarquía.

Desde el punto de vista del mantenimiento, cada cambio de la API va a
suponer que el consumidor de la librería tenga que adaptar su código a
los cambios de API. Estos cambios son más fáciles, casi triviales, en
el caso de la inyección, pero bastante más difíciles con la herencia
si requiere mantener varias ramas paralelas en la jerarquía de clases.

Desde el punto de vista del creador de la librería, los decoradores de
clase son una buena solución para agrupar en un mismo sitio todos los
cambios derivados de un cambio de API. Conceptualmente, en lugar de
modificar la jerarquía de clases con cada cambio, se pondría a
disposición del cliente decoradores para adaptar sus clases a los
cambios de cada API.

Tal vez, la solución no sea ni especializar ni inyectar, si no una
mezcla de ambas.


>
> On 2013-10-22 04:14, Hernan M. F. wrote:
>>>
>>> gracias a todos por el interes. Creo que me ha quedado bastante claro el
>>> asunto.
>>>
>>> - Yeiniel me ha gustado tu solución, solo que yo la utilizaría con la
>>> sintaxis que ha utilizado Juan.
>>> - Sergio, no estoy intentando resolver ningún problema, solamente
>>> estoy "jugando" con los decoradores y viendo de lo que son capaces. Y
>>> mi pregunta surge desde ese interes.
>>> - Y enlazando la frase anterior, gracias Txema por tu post, ya que
>>> bien explicas para que son bueno los decoradores y para que no.
>>
>>
>> Ten cuidado cuando cambies el comportamiento de objetos al vuelo.
>>
>> Si vas a componer clases ¿por qué complicarse?. Usa lo estándar:
>>   class C (B):
>>      def __init__(self):
>>         self._f_provider = D()
>>      def F(self):
>>         self._f_provider.F()
>>
>> Tampoco estás obligado a definir una clase y usar métodos, esto no es
>> Java.
>> F() podría ser un procedimiento o función de un módulo.
>>
>> Con la herencia múltiple de Python (que a veces se nos olvida que tiene),
>> sería:
>> 'class C (B,D)' y no tienes que hacer mas nada. Eso sí, te compras
>> otros ocho mil
>> problemas nuevos…
>>
>> Y si el problema y el marco de la solución lo merece lo mas formal es usar
>> abc.
>>
>> Keep it simple. ;-)
>>
>>>
>>> El día 21 de octubre de 2013 18:48, Txema Vicente <txema en nabla.net>
>>> escribió:
>>>>
>>>> Buenas.
>>>>
>>>> Aunque puedas usar decoradores para ampliar la clase que decoran, yo no
>>>> veo
>>>> los decoradores como sustitutos de la herencia, ni ninguna reduccion de
>>>> codigo.
>>>>
>>>> No necesitas decoradores para hacer eso, puedes asignar una funcion a un
>>>> atributo de la clase (B.F = F). Ademas, como te pongas a crear clases
>>>> decoradas que se amplian en ejecucion, a ver como lo explicas luego.
>>>>
>>>> Los decoradores vienen bien, por ejemplo, para "enchufar" funciones que
>>>> van
>>>> a manejar algo, como funciones que van a tratar los eventos de un GUI, o
>>>> responder en una ruta URL @ruta("/admin"). Dependiendo de lo que quieras
>>>> hacer, sera con una funcion o con una clase, con argumentos o sin ellos.
>>>>
>>>> Tambien tienes el decorador @classmethod por si quieres crear clases que
>>>> puedan tener casos particulares (miclase = B.ampliada_con_F()), o actuar
>>>> como "factoria" de clases.
>>>> Y @staticmethod, que yo solo lo uso en raras ocasiones por motivos de
>>>> organizacion de API.
>>>>
>>>> La herencia es algo claro y maravilloso que te permite organizar las
>>>> cosas.
>>>> El decorador es un "atajo del idioma" para trastear con las funciones,
>>>> no
>>>> hay nada que realmente no puedas hacer sin usarlo.
>>>>
>>>>
>>>> El 21/10/2013 15:37, Ander Garmendia escribió:
>>>>
>>>> Buenas,
>>>>
>>>> estoy 'jugando' con decoradores y haciendo diferentes pruebas y tengo
>>>> una duda que quizá alguien me pueda aclarar.
>>>>
>>>> Digamos que tenemos una clase ( llamemosla B ) a la que queremos
>>>> añadir una funcionalidad (llamemosla F). El método clásico sería
>>>> heredar desde la clase base ( B ) y crear una nueva clase ( llamemosla
>>>> C ) que implementase nuestra funcionalidad ( F ). Hasta aquí todo
>>>> normal y corriente.
>>>>
>>>> Ahora llega python y nos ofrece los decoradores, por lo tanto, podemos
>>>> crear una clase decoradora ( llamemosla D ) que implemente la
>>>> funcionalidad ( F ) y que decorando una clase ( volvamos a la clase B
>>>> ), añade la funcionalidad F en la clase B sin necesidad de herencias
>>>> de ningún tipo.
>>>>
>>>> Visto así, todo parece muy cómodo, se escribe menos código, hay menos
>>>> clases implicadas, etc.
>>>> Y como todo parece muy bonito, aquí surge mi duda: ¿Está esta practica
>>>> extendida al escribir código en python ( es pythonico y aceptable ) ?
>>>> ¿ o es mas una prueba conceptual ?
>>>>
>>>> Gracias de antemano y un saludo.
>>>>
>>>> Ander.
>>>> _______________________________________________
>>>> Python-es mailing list
>>>> Python-es en python.org
>>>> https://mail.python.org/mailman/listinfo/python-es
>>>> FAQ: http://python-es-faq.wikidot.com/
>>>>
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> Python-es mailing list
>>>> Python-es en python.org
>>>> https://mail.python.org/mailman/listinfo/python-es
>>>> FAQ: http://python-es-faq.wikidot.com/
>>>>
>>> _______________________________________________
>>> Python-es mailing list
>>> Python-es en python.org
>>> https://mail.python.org/mailman/listinfo/python-es
>>> FAQ: http://python-es-faq.wikidot.com/
>>
>>
>> _______________________________________________
>> Python-es mailing list
>> Python-es en python.org
>> https://mail.python.org/mailman/listinfo/python-es
>> FAQ: http://python-es-faq.wikidot.com/
>
>
> --
>
> _______________________________________________
> Python-es mailing list
> Python-es en python.org
> https://mail.python.org/mailman/listinfo/python-es
> FAQ: http://python-es-faq.wikidot.com/



-- 
Hyperreals *R  "Quarks, bits y otras criaturas infinitesimales":
http://ch3m4.org/blog
Buscador Python Hispano: http://ch3m4.org/python-es


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