Fwd: Decoradores

Rodrigo Gallardo rodrigo en nul-unu.com
Vie Oct 24 17:54:14 CEST 2008


On Fri, Oct 24, 2008 at 05:06:55PM +0200, Cesar Ortiz wrote:
> *"Decorators allow you to inject or modify code in functions or classes. ...
> For example, suppose you'd like to do something at the entry and exit points
> of a function (such as perform some kind of security, tracing, locking, etc.
> -- all the standard arguments for AOP)."*
> 
> Decorators te permiten añadir código a funciones o clases; o modificarlo ...
> Por ejemplo, imagina que te gustaría hacer algo en los puntos de entrada y
> salida de una función (como realizar algun tipo de 'seguridad', traceo,
> locking, etc. -- todos los argumentos estandares para AOP)".

Un ejemplo concreto, que acabo de, para mi dolor, implementar en Java:

Tengo una clase que sabe hacer llamadas via HTTP a unos servidores remotos
que tienen datos interesantes. Llamemosla ApiCaller.

Tengo otra clase que sabe transformar estos datos interesantes a otro formato.
La llamaré DataTransformer.

En general, las secuencias de llamadas son algo así como:

class DataTransformer:

 def getPeople(id, viewer, sortOptions):

   apiData = ApiCaller.getPeople(id, viewer)
   myData = transform(apiData)

   myData.sort(getSorter(sortOptions)

   return myData


Limpio y claro, ¿no?

Lo malo, es que las llamadas al API remoto son muy tardadas, así que nos gustaría
guardar los resultados en un cache. La forma evidente es sustituir

  apiData = ApiCaller.getPeople(id, viewer)

por

  cacheKey = Cache.makeKey("getpeople", id, viewer)
  apiData = Cache.lookUp(cacheKey)

  if not apiData:
    apiData = ApiCaller.getPeople(id, viewer)
    Cache.store(cacheKey, apiData)


Pero, hay *muchas* secciones como ésta en el código. Además de aburrido, si lo haces
así *seguramente* vas a equivocarte alguna vez. Vas a calcular la llave equivocada
para el cache, o vas a olvidar hacer el store, o ...

En vez de eso, escribes un decorador que encapsula esto:
(Aquí es donde el ejemplo se vuelve aún más abstracto, por que no recuerdo bien la
sintaxis pythonica de esto. Perdón.)

def Cacheable(function): 
 # function es la funcion cuyos resultados vamos a cachear.
 # Un decorador funciona regresando una función que sustituye todas las llamadas
 # a la original, así que lo que hacemos aquí es definir esa función y regresarla

 def cached(*args):
   cacheKey = Cache.makeKey(function.name, args)
   cachedData = Cache.lookUp(cacheKey)

   if cachedData: return cachedData

   data = function(args)
   Cache.store(cacheKey, data)
  
   return data

 return cached


Y después vas a la clase ApiCaller y marcas todos los métodos "cacheables"

class ApiCaller:

 @Cacheable
 def getPeople(id, viewer):
 ...


 @Cacheable
 def getFurryAnimals(species, size):
 ...


¡Y listo! Nada que cambiar en ningún otro lado. Si por ejemplo decides que 
guardar a cache se debe hacer en otra hebra, lo haces en el decorador y ya.
Si algunos métodos requieren diferentes opciones de cacheo, los pones como
argumentos al decorador. Si además de mandar a cache quieres guardar
estadísticas de desempeño, pones otro decorador que hace eso, etc, etc,
etc.

Y si estás programando en Java, lloras y lloras por lo doloroso que resulta
comparado con Python, pero lo haces de todos modos por que las alternativas
son peores :) 

Espero explique un poco.
_______________________________________________
Lista de correo Python-es 
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes





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