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

Yeiniel Suárez Sosa yeiniel en uclv.cu
Mie Oct 23 14:57:40 CEST 2013


ahh, me falto decir (haciendo un poco de promocion)

que el ejemplo anterior que envie es tomado de 
https://github.com/yeiniel/aurora

atentamente
Yeiniel

On 2013-10-23 05:54, Ander Garmendia wrote:
> 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/
> _______________________________________________
> Python-es mailing list
> Python-es en python.org
> https://mail.python.org/mailman/listinfo/python-es
> FAQ: http://python-es-faq.wikidot.com/

-- 
Ing. Yeiniel Suárez Sosa
Profesor Instructor, Dep. Automática
FIE, UCLV


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