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/

Responder a