Aplicar un decorador a todas las funciones de un módulo en Python
Este post fue migrado de un blog hecho con Wordpress. Si se ve mal, dejame un comentario y lo arreglo.
En la lista de PyAr preguntaron si había alguna forma de aplicar un decorador a todos las funciones de un módulo. Envié una solución sin probarla, que al verla unos días más tarde parece bastante buena :)
La comento aquí con un ejemplo. modulo.py contiene definiciones de funciones:
def a():passdef b():
print 42def c():
a() b()</pre>y decoradores.py un decorador que imprime el nombre de la función llamada:
def nombrador(f): def inner(*a, **kw): print "Ejecutando %s" % f.__name__ return f(*a, **kw) return inner(Si no sabés lo que es un decorador, podés leer mi post Decoradores en Python I: Introducción)
En lugar de modificar las definiciones de funciones en modulo.py para aplicar el decorador a cada una de las funciones, ya sea usando el azúcar sintáctica de Python:
@nombrador def a(): ...o mediante una llamada a la función:
a = nombrador(a)podemos agregar el siguiente código al final de modulo.py:
for n,v in locals().items(): if inspect.isfunction(v) and n != 'nombrador': locals()[n] = nombrador(v)Vamos a explicarlo:
la llamada a la función built-in locals retorna un diccionario representando el espacio de nombres local: cada clave es un string representando el nombre de un objeto y cada valor es el objeto en si. Iteramos sobre la lista de pares (key, value) del mencionado dict y por cada uno verificamos si:
a) es una función (inspect.isfunction es apropiado para esto)
b) el nombre no es el del decorador que queremos aplicar (para no aplicar el decorador sobre si mismo!)
Si las condiciones a y b se cumplen, podemos guardar en el diccionario del espacio de nombres, bajo el nombre de la función que cumplió las condiciones, una versión decorada de la misma.
Agregamos algo más de código a modulo.py para que se llame a las funciones cuando lo ejecutemos:
if __name__ == '__main__': a() b() c()Esta es la salida obtenida:
juanjo@fenix:~/python/muchosdecos$ python modulo.py Ejecutando a Ejecutando b 42 Ejecutando c Ejecutando a Ejecutando b 42¿Querés probarlo? Bajá muchos.zip
Nota: para acceder a locals() no se puede utilizar iteritems por que el diccionario cambia durante la ejecución.
Comentarios
Comments powered by Disqus