Hernan, hola
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)
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/