El día 23 de octubre de 2013 14:53, Yeiniel Suárez Sosa <yein...@uclv.cu> escribió: > Hernan, hola
Supongo que en realidad me estás respondiendo a mí. > solo quiero llamarte la atención que en el ejemplo que pones, solo es > posible modificar tu clase C por herencia y no por inyección. Por eso es que > yo no declaro mis componentes abstractos, solo que para crearlos necesitas > pasarles sus dependencias en el constructor o si son opcionales reescribo > los atributos en el objeto. > > Por eso para las dependencias estrictas (las cosas sin las cuales el objeto > no puede funcionar) pondo un estub que simplemente dice que no esta > implementado para forzar al desarrollador a inyectar una implementacion para > el metodo (servicio) Con la clase abstracta tan sólo quería sugerir un modo de resolver estos temas, tanto si vas a usar herencia como inyección. En el caso de la inyección, un modo simple de garantizar que el objeto que se pasa al constructor responde a los métodos requeridos sería ofrecer una clase abstracta para que el usuario la especialice, lo que viene a ser: aplicar un patrón de diseño "Decorador". import abc class D: __metaclass__=abc.ABCMeta @abc.abstractmethod def f(self): pass class C: class _Def(D): def f(self): print "No parcheado" def __init__(self, obj=_Def()): assert(isinstance(obj, D)) self.obj=obj def f(self): self.obj.f() Con ofrecer al cliente la clase abstracta D tendrías asegurado que todo va a ir como se espera. Incluso se puede rizar el rizo añadiendo un decorador a la clase C: def inject(self, fx): class Dx(D): def f(self): fx(self) self.obj=Dx() return fx Y usarlo así: c=C() c.f() #-->No parcheado @c.inject def f(self): print "Parcheado" c.f() #-->Parcheado Seguramente habrá muchas otras formas. > > atentamente > Yeiniel > > > On 2013-10-23 05:56, Chema Cortes wrote: >> >> El día 22 de octubre de 2013 14:50, Yeiniel Suárez Sosa >> <yein...@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 <tx...@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@python.org >>>>>> https://mail.python.org/mailman/listinfo/python-es >>>>>> FAQ: http://python-es-faq.wikidot.com/ >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> Python-es mailing list >>>>>> Python-es@python.org >>>>>> https://mail.python.org/mailman/listinfo/python-es >>>>>> FAQ: http://python-es-faq.wikidot.com/ >>>>>> >>>>> _______________________________________________ >>>>> Python-es mailing list >>>>> Python-es@python.org >>>>> https://mail.python.org/mailman/listinfo/python-es >>>>> FAQ: http://python-es-faq.wikidot.com/ >>>> >>>> >>>> >>>> _______________________________________________ >>>> Python-es mailing list >>>> Python-es@python.org >>>> https://mail.python.org/mailman/listinfo/python-es >>>> FAQ: http://python-es-faq.wikidot.com/ >>> >>> >>> >>> -- >>> >>> _______________________________________________ >>> Python-es mailing list >>> Python-es@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 > > _______________________________________________ > Python-es mailing list > Python-es@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 _______________________________________________ Python-es mailing list Python-es@python.org https://mail.python.org/mailman/listinfo/python-es FAQ: http://python-es-faq.wikidot.com/