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

Sergio Fernández sergio en wikier.org
Lun Oct 21 18:05:06 CEST 2013


A ese respecto, hay un detalle que me parece interesante comentar.
Quizá sea un detalle que parezca irrelevante, pero creo que es
interesante comprender bien como funciona internamente el api de
reflectividad en python si uno no se quiere topar con errores a los
que no se les es capaz de encontrar el sentido. Y lo digo por
experiencia ;-)

En python, cuando se le pide un atributo/método a un objeto, en
realidad el método __getattr__ es el encargado de buscarlo, bien en el
propio objeto instancia o en su jerarquía de clases. Este método
devuelve el atributo (o método) si lo encuentra, o lanza una excepción
AttributeError. Para evitar llamadas recursivas y provocar errores en
caso de no encontrarse, el modelo reflectivo presenta una asimetría
entre __getattr__ y __setattr__:

* __getattr__ no se llama si el atributo no se encuentra por las vías
convencionales

* para tener control total sobre la operación es necesario recurrir al
método __getattribute__ y así evitar esos problemas de recursividad
infinita; este método siempre debe invocar el método de la clase base.

No sé cual es el problema concreto que necesita resolve Ander, pero
esta es mi receta para tener control total en entornos con uso
extensivo de la reflectividad unido a jerárquias complejas de clases:

obj.__dict__[’__setattr__’] = MethodType(set_func, obj, obj.__class__)
obj.__dict__[’__getattribute__’] = MethodType(get_func, obj, obj.__class__)

Puede parece una manera un tanto enrevesada en lugar de usar el simple
built-in setattr(), pero hay situaciones que exigen tal tratamiento.
Al menos python lo permite, tratar de hacer eso con
java.lang.reflection xD


2013/10/21 Juan BC <jbc.develop en gmail.com>:
> setattr(b, "mi_servicio", a.mi_servicio)
>
> El día 21 de octubre de 2013 11:30, Yeiniel Suárez Sosa
> <yeiniel en uclv.cu> escribió:
>> Hola
>>
>> como lo que pregunta es si la práctica está extendida, le voy a responder
>> con mi práctica.
>> Normalmente no creo un decorador para esto. Supongamos que tengo una clase A
>> que
>> ofrece un servicio mi_servicio (un método) y una clase B que desea usar el
>> servicio
>> que ofrece A (no es una herencia, sino una composición). En este caso yo en
>> la clase B escribo
>> una declaración de la misma función con el mismo nombre, los mismos
>> parámetros, las mismas
>> anotaciones (si usas Python 3.x) solo que le pongo en el cuerpo "raise
>> NotImplementedError()" y en la
>> documentación indico que debe ser un método compatible con "A.mi_servicio"
>> fijense que digo compatible, aqui dejo
>> abierta la posibilidad para muchas cosas.
>> De esta forma indico que B consume un servicio pero que no lo implementa
>> esta clase. A la hora de usar B simplemente creo
>> el objeto de la clase B e inyecto una implementación para el método
>> mi_servicio.
>>
>> Codigo de Ejemplo:
>> ###################################
>> class A:
>>   def mi_servicio(self):
>>     print("servicio que imprime un mensaje en pantalla")
>>
>> class B:
>>   def mi_servicio(self):
>>     """
>>     Como implementacion de este metodo puede usarse A.mi_servicio o
>>     cualquier otro compatible.
>>     """
>>     raise NotImplementedError()
>>
>> # luego cuando deseo usar B
>> a = A()
>> b = B()
>> b.mi_servicio = a.mi_servicio
>> ##################################
>> La ultima parte de creación del objeto b e inyección de dependencias lo hago
>> utilizando una
>> librería de inyección de dependencias sencilla de solo dos funciones
>> (inject() y create_descriptor())
>>
>> En el ejemplo de arriba se asume que mi_servicio es opcional para B, en caso
>> de ser requerido lo
>> adiciono como un argumento de inicialización de la clase B y paso
>> a.mi_servicio como argumento del
>> constructor.
>>
>> Espero comprendas que inyección de dependencias no quiere decir emplear un
>> contenedor, ni una librería,
>> sino dejar bien claro quien requiere algo que lo ofrece otro.
>>
>> Atentamente
>> --
>> Ing. Yeiniel Suárez Sosa
>> Profesor Instructor, Dep. Automática
>> FIE, UCLV
>>
>>
>> On 2013-10-21 09:37, Ander Garmendia wrote:
>>>
>>> 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/
>
>
>
> --
> Juan B Cabral
> _______________________________________________
> Python-es mailing list
> Python-es en python.org
> https://mail.python.org/mailman/listinfo/python-es
> FAQ: http://python-es-faq.wikidot.com/



-- 

Sergio Fernández <sergio en wikier.org>


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