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