Decorando con python (decoradores)
Python es un lenguaje con el que se pueden programar grandes cosas sin tener que conocer nada más que un par de cosas, pero que si te pones a aprender vas aprendiendo diferentes formas de hacer las cosas.
En python todo es un objeto. Esto que parece una tontería, en realidad lo es. Todo un objeto, guay, una función es un objeto con un método __call__. Por lo tanto puedo hacer una función que reciba como argumento una función y devuelva una función. Y eso está guay, y es uno de los fundamentos de la programación funcional.
Y te preguntarás, ¿Para qué puedo querer yo una función que reciba una función? Pues eso es un "decorador" en python. Y tienen su utilidad. Y da la casualidad de que cuando no conoces algo no tienes la necesidad de usarlo, pero cuando lo conoces dices: "¿cómo he podido vivir yo sin esto?".
Así pues en cuanto supe de los decoradores en python empecé a verle utilidad en cada programa que hago.
¿Y a qué viene todo esto? Vamos a ver algún ejemplo práctico que he usado en geco.
Por ejemplo, para el cliente web yo tenía varias funciones en las cuales hacía la misma comprobación:
def GET(self, args):
username = session.get('username', '')
if username:
...
(aquí lo importante de la función)
...
else:
raise web.seeother('/login')
Para evitar la repetición de este código se puede usar un decorador, y la cosa quedaría así:
@authenticated
def GET(self, args):
...
(aquí lo importante de la función)
...
Si esto se escala a varias funciones claramente se ve que nos ahorramos un buen cacho de código (no tanto :P ), y la parte realmente importante de la función queda mucho más clara.
authenticated es una función que recibe una función y devuelve una función, y que con la sintaxis esta tan bonita de la arroba no quiere decir más que esto:
def GET(self, args):
...
(aquí lo importante de la función)
...
GET = authenticated(GET)
Pero con la sintaxis de la arroba queda más bonito. Por otra parte queda ver el código de la función decoradora:
def authenticated(function):
session = web.ses
def new_function(*args, **kwargs):
username = session.get('username', '')
if username:
return function(*args, **kwargs)
else:
raise web.seeother('/login')
return new_function
Es una función, en la cual se define una función anidada, new_function, que recibe un número indeterminado de argumentos sin nombre (*args) y un número indeterminado de argumentos con nombre (*kwargs), y desde esta función anidada se hace una llamada a la función original que es la que se recibe por parámetro.
Otras cosas interesantes de python
Cuando en una función se define un parámetro con asterisco (*args) quiere decir que esta función puede recibir varios argumentos. Estos argumentos se almacenan en una lista de nombre args. El operador asterisco también sirve para desplegar una lista en argumentos a una función, por ejemplo una función que reciba dos argumentos "def f(x, y): pass" puede ser llamada pasándole una lista como
argumento "a = (1, 2)", ">>> f(*a)".
Lo mismo que lo anterior pasa cuando se ponen dos asteriscos, pero en lugar de tratar con argumentos sin nombre tratamos con argumentos con nombre y en lugar de una lista tenemos un diccionario. Por ejemplo:
def f(x=1, y=2):
pass
d = {"x": 3, "y": 5}
f(**d)
-> pass -> no hace nada
-> raise -> sirve para lanzar una excepción
Y ya está, que me enrollo. Pronto pondré más capturas y contaré como va el cliente web, que ya tiene una pinta estupenda :P




Comments
Bueno el tip
Me parecio genial el tip de los parámetros *args y **kwars, si no es mucha molestia lo colocaré en mi blog tal cual como tú lo explicas dando la fuente de tú blog.
No es ninguna molestia,
No es ninguna molestia, distribuye el conocimiento :P
Gracias
por las explicaciones, por cierto en el primer kwargs se te ha pasado un asterísco en la frase "y un número indeterminado de argumentos con nombre (*kwargs)"
Saludos.