Entendiendo Decoradores en Python --------------------------------- +-------------------------------+---------------------------+ |* El principio de todo | | |* ¿Qué es un decorador? | .. image:: senoras.jpg | |* Funciones decoradoras | :scale: 80 % | |* Decoradores con parámetros | :align: right | |* Clases decoradores | | |* Decorar clases | | +-------------------------------+---------------------------+ El principio de todo -------------------- +-----------------------------+-----------------------------+ | .. image:: flor.jpg |Todo en Python es un objeto | | :scale: 80 % | | | :align: right |* Identidad | | |* Tipo | | |* Valor | +-----------------------------+-----------------------------+ Objetos -------- .. code-block:: pycon >>> a = 1 >>> id(a) 145217376 >>> a.__add__(2) 3 Otros objetos: .. code-block:: python [1, 2, 3] # listas 5.2 # flotantes "hola" # strings Funciones ---------- Las funciones también son objetos. .. code-block:: python def saludo(): print "hola" .. code-block:: pycon >>> id(saludo) 3068236156L >>> saludo.__name__ 'saludo' >>> dice_hola = saludo >>> dice_hola() hola Decorador (definición no estricta) ---------------------------------- Un decorador es una *función* **d** que recibe como parámetro otra *función* **a** y retorna una nueva *función* **r**. .. raw:: pdf Spacer 0 24 * d: función decoradora * a: función a decorar * r: función decorada .. code-block:: python a = d(a) Código ------- .. code-block:: python def d(a): def r(*args, **kwargs): # comportamiento previo a la ejecución de a a(*args, **kwargs) # comportamiento posterior a la ejecución de a return r Código -------- .. code-block:: python def d(a): def r(*args, **kwargs): print "Inicio ejecucion de", a.__name__ a(*args, **kwargs) print "Fin ejecucion de", a.__name__ return r Manipulando funciones --------------------- .. code-block:: python def suma(a, b): print a + b .. code-block:: pycon >>> suma(1,2) 3 >>> suma2 = d(suma) >>> suma2(1,2) Inicio ejecucion de suma 3 Fin ejecucion de suma >>> suma = d(suma) >>> suma(1, 2) Inicio ejecucion de suma 3 Fin ejecucion de suma Azúcar sintáctica ----------------- A partir de Python 2.4 se incorporó la notación con @ para los decoradores de funciones. .. code-block:: python def suma(a, b): return a + b suma = d(suma) .. code-block:: python @d def suma(a, b): return a + b Atención -------- Antiejemplo: el decorador malvado. .. code-block:: python def malvado(f): return False .. code-block:: pycon >>> @malvado ... def algo(): ... return 42 ... >>> algo False >>> algo() Traceback (most recent call last): File "", line 1, in TypeError: 'bool' object is not callable Decoradores en cadenados ------------------------ Similar al concepto matemático de componer funciones. .. code-block:: python @registrar_uso @medir_tiempo_ejecucion def mi_funcion(algunos, argumentos): # cuerpo de la funcion .. code-block:: python def mi_funcion(algunos, argumentos): # cuerpo de la funcion mi_funcion = registrar_uso(medir_tiempo_ejecucion(mi_funcion)) Decoradores con parámetros -------------------------- * Permiten tener decoradores más flexibles. * Ejemplo: un decorador que fuerce el tipo de retorno de una función. .. code-block:: python @to_string def count(): return 42 .. code-block:: pycon >>> count() '42' Decoradores con parámetros -------------------------- Primera aproximación. .. code-block:: python def to_string(f): def inner(*args, **kwargs): return str(f(*args, **kwargs)) return inner Decoradores con parámetros -------------------------- Algo más genérico? .. code-block:: python @typer(str) def c(): return 42 @typer(int) def edad(): return 25.5 .. code-block:: pycon >>> edad() 25 Decoradores con parámetros -------------------------- ``typer`` es una fábrica de decoradores. .. code-block:: python def typer(t): def _typer(f): def inner(*args, **kwargs): r = f(*args, **kwargs) return t(r) return inner return _typer Clases decoradoras ------------------ * Decoradores con estado. * Código mejor organizado. .. code-block:: python class Decorador(object): def __init__(self, a): self.variable = None self.a = a def __call__(self, *args, **kwargs): # comportamiento previo a la ejecución de a self.a(*args, **kwargs) # comportamiento posterior a la ejecución de a Clases decoradoras ------------------ .. code-block:: python @Decorador def nueva_funcion(algunos, parametros): # cuerpo de la funcion * Se instancia un objeto del tipo ``Decorador`` con ``nueva_función`` como argumento. * Cuando llamamos a ``nueva_funcion`` se ejecuta el método ``__call__`` del objeto instanciado. .. code-block:: python def nueva_funcion(algunos, parametros): # cuerpo de la funcion nueva_funcion = Decorador(nueva_funcion) Decorador (definición más estricta) ----------------------------------- Un decorador es una *callable* **d** que recibe como parámetro un *objeto* **a** y retorna un nuevo objeto **r** (por lo general del mismo tipo que el orginal o con su misma interfaz). .. raw:: pdf Spacer 0 24 * d: objeto de un tipo que defina el método ``__call__`` * a: cualquier objeto * r: objeto decorado .. code-block:: python a = d(a) Decorar clases (Python >= 2.6) ------------------------------ Identidad: .. code-block:: python def identidad(C): return C .. code-block:: pycon >>> @identidad ... class A(object): ... pass ... >>> A() <__main__.A object at 0xb7d0db2c> Decorar clases (Python >= 2.6) ------------------------------ Cambiar totalmente una clase: .. code-block:: python def abuse(C): return "hola" .. code-block:: pycon >>> @abuse ... class A(object): ... pass ... >>> A() Traceback (most recent call last): File "", line 1, in TypeError: 'str' object is not callable >>> A 'hola' Decorar clases (Python >= 2.6) ------------------------------ Reemplazar con una nueva clase: .. code-block:: python def reemplazar_con_X(C): class X(): pass return X .. code-block:: pycon >>> @reemplazar_con_X ... class MiClase(): ... pass ... >>> MiClase Decorar clases (Python >= 2.6) ------------------------------ Instancia: .. code-block:: python def instanciar(C): return C() .. code-block:: pycon >>> @instanciar ... class MiClase(): ... pass ... >>> MiClase <__main__.MiClase instance at 0xb7d0db2c> Dónde encontramos decoradores? ------------------------------ Permisos en Django .. code-block:: python @login_required def my_view(request): ... URL routing en Bottle .. code-block:: python @route('/') def index(): return 'Hello World!' Standard library .. code-block:: python classmethod, staticmethod, property Muchas gracias! --------------- .. image:: mister.jpg :scale: 80 % :align: center Datos y contacto ---------------- * Comentarios, dudas, sugerencias: jjconti@gmail.com * Blog: http://www.juanjoconti.com.ar * Twitter: @jjconti .. raw:: pdf Spacer 0 24 * http://www.juanjoconti.com.ar/categoria/aprendiendo-python/ * http://www.juanjoconti.com.ar/2008/07/11/decoradores-en-python-i/ * http://www.juanjoconti.com.ar/2009/07/16/decoradores-en-python-ii/ * http://www.juanjoconti.com.ar/2009/12/30/decoradores-en-python-iii/ * http://www.juanjoconti.com.ar/2010/08/07/functools-update_wrapper/ .. header:: Entendiendo Decoradores en Python .. footer:: Juan José Conti - PyDay 2011 - Córdoba - 30/04/2011