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

Ander Garmendia kelertxiki en gmail.com
Mie Oct 23 11:54:28 CEST 2013


Buenos dias,

para empezar, Yeiniel, por favor, tuteame que si me siento mas viejo ;)

Siguiendo con el tema, lo que tenía yo en mente, era el segundo caso
que has citado en el mensaje anterior: esto es B quiere exponer una
funcionalidad F.

Al respecto, un ejemplo, sería poder añadir conectividad con  BBDD sin
tener que modificar la clase B. El decorador se encargaria de
gestionar la conexión y ejecutar las querys.

Quizá este no sea el mejor ejemplo ( yo no lo haría de esa manera )
pero creo que resume bien lo que planteé en el primer mensaje.
Simplificando, el código sería el siguiente:

class Decor(object):
    '''Decora la clase para anadir funcionalidades'''
    def __init__(self, arg):
        self.arg = arg
        print "Clase decoradora"
        print self.arg

    def __call__(self, cls):
        class Wrapper(cls):
            classattr = self.arg
            print "En wrapper de clase decoradora"

            def my_method(self, value):
                print "New my method {0}, conn string:
{1}".format(value, self.classattr)

            def new_method(self, value):
                print "New new method {0}".format(value)

        return Wrapper


@Decor('mysql')
class B(object):
    def __init__(self, nombre):
        self.nombre = nombre
        print "Creo objeto B"

    def show_name(self):
        print "My nombre es {0}".format(self.izena)

    def __call__(self):
        print "Inside call"


a = B('Ander')
a.my_method('Ander')
a.new_method('Ander2')

El código es válido y anade las funciones a la clase decorada, pero no
me acaba de gustar y es por eso por lo que formulé la pregunta.

Un saludo.

Ander.

El día 22 de octubre de 2013 14:50, Yeiniel Suárez Sosa
<yeiniel en uclv.cu> escribió:
> Hola nuevamente
> Yo normalmente no escribo en esta lista, lo cual es malo para la comunidad y
> me disculpo, pero estoy haciéndolo ahora porque el tema es teórico y merece
> la pena (al menos desde mi punto de vista) lograr buenas prácticas en la
> comunidad.
>
> Ander Garmendia
> en el post original usted dice que quiere añadir una funcionalidad F a una
> clase B. Aquí es necesario dejar bien claro si el problema es que B necesita
> la funcionalidad F para hacer su trabajo o si B tiene que exponer la
> funcionalidad F. En el primer caso estamos tratando con un problema de
> dependencia, del cual expuse mi forma de tratarlo. Si el problema es que B
> necesita exponer a F, bueno el problema es otro, porque todo depende de que
> tipo de elemento es F. La herencia es para crear objetos que son una
> especialización de otro más general, que B herede de C solo es correcto si B
> es un caso especifico de C, de lo contrario la herencia no tiene sentido. En
> el caso de que B necesite exponer un método de C pero no es una
> especialización de esta clase, entonces el problema todavía es de
> dependencia (composición) y no de herencia.
>
> ahh, se me olvidaba, la sintaxis correcta para inyectar atributos a la clase
> es la de Juan, saludos para el
>
> 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. 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.
>
> Atentamente
> Ing. Yeiniel Suárez Sosa
> Profesor Instructor, Dep. Automática y Sistemas Computacionales
> Facultad de Ingeniería Eléctrica, Universidad Central "Marta Abreu" de las
> Villas (UCLV)
>
>
> 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/


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