[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