Introducci贸n a Decoradores (Decorators) en Python露
Introducci贸n露
Los decorators constituyen un patr贸n de programaci贸n que se utiliza cuando es necesario incluir un comportamiento adicional a objetos espec铆ficos. Una forma de agregar tal comportamiento adicional es decorar los objetos creados con tipos que aportan la funcionalidad extra.
Estos decoradores envuelven el objeto original pero presentan exactamente la misma interfaz para el usuario de ese objeto. Por lo tanto, el patr贸n de dise帽o del decorador extiende el comportamiento de un objeto sin utilizar subclassing. La decoraci贸n de un objeto es transparente a los clientes de los decoradores.
En Python, los decoradores son funciones que toman otra funci贸n (u otro objeto invocable como un m茅todo) y devuelve una tercera funci贸n que representa el comportamiento decorado.
Decoradores露
Definici贸n de un decorador露
Para definir un decorador, debe definir un objeto invocable, como una funci贸n que toma otra funci贸n como par谩metro y devuelve una nueva funci贸n. A continuaci贸n se da un ejemplo de la definici贸n de una funci贸n decoradora de registro (logger) muy simple
def logger(func):
def inner():
print('llamando ', func.__name__)
func()
print('llamada', func.__name__)
return inner
Observe que la funci贸n logger retorna una funci贸n, inner, la cual a su vez llamar谩 a una tercera funci贸n func.
Usando el decorador露
Veamos ahora el efecto del decorador en acci贸n. Usaremos la funci贸n target como la funci贸n que vamos a decorar.
def target():
print('Dentro de la funci贸n target')
t1 = logger(target)
t1()
llamando target
Dentro de la funci贸n target
llamada target
Suavizando el trabajo露
Python proporciona algo de az煤car sint谩ctico que permite decorar directamente la funci贸n desde su definici贸n. Este es el uso m谩s practico de los decoradores.
@logger
def target():
print('Dentro de la funci贸n target')
target()
llamando target
Dentro de la funci贸n target
llamada target
Funciones con par谩metros露
En este caso la funci贸n decoradora debe incluir los par谩metros. Veamos el ejemplo.
def logger(func):
def inner(x, y):
print('llamando ', func.__name__, 'con ',x , 'y',y)
func(x, y)
print('regresando de ',func.__name__)
return inner
@logger
def mi_funcion(x, y):
print('x + y = ', x+y)
mi_funcion(5,6)
llamando mi_funcion con 5 y 6
x + y = 11
regresando de mi_funcion
Decoradores apilados (stacked decorators)露
Es posible apilar decoradores. Veamos el ejemplo. Vamos a imprimir un texto. Los decoradores agregaran negrilla (bold) e it谩lica (italic) al texto impreso.
# decoradores
def make_bold(fn):
def makebold_wrapper():
return "<b>" + fn() + "</b>"
return makebold_wrapper
def make_italic(fn):
def makeitalic_wrapper():
return "<i>" + fn() + "</i>"
return makeitalic_wrapper
# aplica los decoradores
@make_bold
@make_italic
def hello():
return 'hola mundo'
print(hello())
<b><i>hola mundo</i></b>
Decoradores para m茅todos de clases露
En este caso, es importante recordar que los m茅todos toman el par谩metro especial self como el primer par谩metro que se utiliza para hacer referencia al objeto del que se est谩 aplicando el m茅todo. Por lo tanto, es necesario que el decorador tome este par谩metro en cuenta; es decir, la funci贸n envuelta interna debe tomar al menos un par谩metro que representa a self. Veamos el ejemplo.
def pretty_print(method):
def method_wrapper(self):
return "<p>{0}</p>".format(method(self))
return method_wrapper
class Persona:
def __init__(self, nombre, apellido, edad):
self.nombre = nombre
self.apellido = apellido
self.edad = edad
def print_self(self):
print('Persona - ', self.nombre, ', ', self.edad)
@pretty_print
def get_nombre_completo(self):
return self.nombre + " " + self.apellido
print('Comenzamos')
p = Persona('Alvaro', 'Montenegro', 61)
p.print_self()
print(p.get_nombre_completo())
print('Hecho!')
Comenzamos
Persona - Alvaro , 61
<p>Alvaro Montenegro</p>
Hecho!
Decoradores para m茅todos de clases con par谩metros露
Aqu铆 combinamos lo hecho en las dos subsecciones anteriores. Veamos.
def trace(method):
def method_wrapper(self, x, y):
print('Llamando', method.__name__, 'con', x, y)
method(self, x, y)
print('Se llam贸', method.__name__, 'con', x, y)
return method_wrapper
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@trace
def move_to(self, x, y):
self.x = x
self.y = y
def __str__(self):
return 'Point - ' + str(self.x) + ',' + str(self.y)
p = Point(1, 1)
print(p)
p.move_to(5,5)
print(p)
Point - 1,1
Llamando move_to con 5 5
Se llam贸 move_to con 5 5
Point - 5,5
Autores露
Alvaro Mauricio Montenegro D铆az, ammontenegrod@unal.edu.co
Daniel Mauricio Montenegro Reyes, dextronomo@gmail.com