def print_hello():
print('{:^30}'.format('Hola mundo pythonico'))Apéndice T — Decoradores.
Objetivo. Explicar como se pueden agregar características a una función de python sin modificarla.
T.1 Definición.
- Se denomina decorador a la persona dedicada a diseñar el interior de oficinas, viviendas o establecimientos comerciales con criterios estéticos y funcionales.
- En Python, un decorador es una función para modificar otra función.
- Recibe una función.
- Regresa otra función.
- Los decoradores son herramientas bonitas y útiles de Python.
# Uso normal de la función
print_hello() Hola mundo pythonico
from colorama import Fore, Back, Style
# Decorador
def decorador1(f):
# La función que hace el decorado.
def envoltura():
linea = chr(0x2015) * 30
print(Fore.BLUE + linea + Style.RESET_ALL)
print(Back.GREEN + Fore.WHITE, end='')
f() # Ejecución de la función que se recibe como parámetro
print(Style.RESET_ALL, end='')
print(Fore.BLUE + linea + Style.RESET_ALL)
return envoltura # Regresamos la función decorada# Decorando la función.
print_hello_colored = decorador1(print_hello) # Funcion decorada
# Ahora se ejecuta la función decorada.
print_hello_colored()―――――――――――――――――――――――――――――― Hola mundo pythonico ――――――――――――――――――――――――――――――
.
# Decorador
def decorador2(f):
# La función que hace el decorado.
# Ahora recibe un parámetro
def envoltura(m):
linea = chr(0x2015) * 30
print(Fore.BLUE + linea + Style.RESET_ALL)
print(Back.GREEN + Fore.WHITE, end='')
f(m) # Ejecución de la función que se recibe como parámetro
print(Style.RESET_ALL, end='')
print(Fore.BLUE + linea + Style.RESET_ALL)
return envoltura # Regresamos la función decorada# La función se puede decorar en su definición como sigue
@decorador2
def print_message(m):
print('{:^30}'.format(m))# Entonces se puede usar la función con su nombre original
print_message('bueno, bonito y barato')―――――――――――――――――――――――――――――― bueno, bonito y barato ――――――――――――――――――――――――――――――
print_message('el bueno, el malo y el feo')―――――――――――――――――――――――――――――― el bueno, el malo y el feo ――――――――――――――――――――――――――――――
.
def decorador3(f):
def coloreado(x):
# Construimos una cadena coloreada con el
# resultado de la evaluación de f(x)
res = Fore.GREEN + f.__name__
res += '(' + Style.BRIGHT + str(x) + Style.RESET_ALL + Fore.GREEN + ') = ' + Style.RESET_ALL
res += f'{f(x):10.8f}' # Aquí se ejectua la función original
# Imprimimos el resultado
linea = chr(0x2015) * 30
print(Fore.BLUE + linea + Style.RESET_ALL)
print('{:}'.format(res))
print(Fore.BLUE + linea + Style.RESET_ALL)
return coloreadofrom math import sin, cos
sin = decorador3(sin)
cos = decorador3(cos)
for f in [sin, cos]:
f(3.1415)―――――――――――――――――――――――――――――― sin(3.1415) = 0.00009265 ―――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――― cos(3.1415) = -1.00000000 ――――――――――――――――――――――――――――――
El decorador se puede usar con otras funciones similares:
from math import factorial, log, e
factorial = decorador3(factorial)
log = decorador3(log)
factorial(5)
log(e)―――――――――――――――――――――――――――――― factorial(5) = 120.00000000 ―――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――― log(2.718281828459045) = 1.00000000 ――――――――――――――――――――――――――――――
.
def decorador4(f):
def envoltura(*args, **kwargs):
# Construimos una cadena coloreada con el
# resultado de la evaluación de f(x)
res = Fore.GREEN + f.__name__
res += '(' + Style.BRIGHT + f'{args},{kwargs}' + Style.RESET_ALL + Fore.GREEN + ') = ' + Style.RESET_ALL
res += f'{f(*args, **kwargs)}'
# Imprimimos el resultado
linea = chr(0x2015) * 30
print(Fore.BLUE + linea + Style.RESET_ALL)
print('{:}'.format(res))
print(Fore.BLUE + linea + Style.RESET_ALL)
return envolturafrom random import random, randint, choice, choices
random = decorador4(random)
randint = decorador4(randint)
choice = decorador4(choice)
choices = decorador4(choices)p = [x for x in range(10)]
random()
randint(3, 30)
choice(p)
choices(p)
choices(p,k=3)―――――――――――――――――――――――――――――― random((),{}) = 0.13026213383709218 ―――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――― randint((3, 30),{}) = 29 ―――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――― choice(([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],),{}) = 7 ―――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――― choices(([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],),{}) = [5] ―――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――― choices(([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],),{'k': 3}) = [2, 8, 1] ――――――――――――――――――――――――――――――
.
import time
def crono(f):
"""
Regresa el tiempo que toma en ejecutarse la función.
"""
def tiempo(N):
t1 = time.perf_counter()
f(N)
t2 = time.perf_counter()
return print(f'Elapsed time: {t2 - t1}\n')
return tiempo@crono
def miFuncion(N):
numeros = []
for num in (range(0, N)):
numeros.append(num)
print('\nLa suma es: ' + str((sum(numeros))))miFuncion(10)
La suma es: 45
Elapsed time: 0.0001438999897800386
.
from time import sleep
def sleepDecorador(function):
def duerme(*args, **kwargs):
sleep(1)
return function(*args, **kwargs)
return duerme@sleepDecorador
def imprimeNumero(num):
return num
for num in range(1, 6):
print(imprimeNumero(num), end = ' ')1 2 3 4 5
.
def checaArgumento(f):
def checador(x):
if type(x) == int and x > 0:
return f(x)
else:
raise Exception("El argumento no es un entero positivo")
return checador@checaArgumento
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)for i in range(1,10):
print(i, factorial(i))1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
print(factorial(-1))--------------------------------------------------------------------------- Exception Traceback (most recent call last) Cell In[24], line 1 ----> 1 print(factorial(-1)) Cell In[21], line 6, in checaArgumento.<locals>.checador(x) 4 return f(x) 5 else: ----> 6 raise Exception("El argumento no es un entero positivo") Exception: El argumento no es un entero positivo
.
def contadorDeLlamadas(func):
def cuenta(*args, **kwargs):
cuenta.llamadas += 1
return func(*args, **kwargs)
# Variable estática que lleva la cuenta
cuenta.llamadas = 0
return cuenta@contadorDeLlamadas
def suma(x):
return x + 1
@contadorDeLlamadas
def mulp1(x, y=1):
return x*y + 1print('Llamadas a suma = {}'.format(suma.llamadas))
print('Llamadas a multp1 = {}'.format(mulp1.llamadas))Llamadas a suma = 0
Llamadas a multp1 = 0
for i in range(4):
suma(i)
mulp1(1, 2)
mulp1(5)
mulp1(y=2, x=25)
print('Llamadas a suma = {}'.format(suma.llamadas))
print('Llamadas a multp1 = {}'.format(mulp1.llamadas))Llamadas a suma = 4
Llamadas a multp1 = 3
.
def buenasTardes(func):
def saludo(x):
print("Hola, buenas tardes, ", end='')
func(x)
return saludo
def buenosDias(func):
def saludo(x):
print("Hola, buenos días, ", end='')
func(x)
return saludo@buenasTardes
def mensaje1(hora):
print("son las " + hora)
mensaje1("3 pm")
@buenosDias
def mensaje2(hora):
print("son las " + hora)
mensaje2("8 am")Hola, buenas tardes, son las 3 pm
Hola, buenos días, son las 8 am
.
def saludo(expr):
def saludoDecorador(func):
def saludoGenerico(x):
print(expr, end='')
func(x)
return saludoGenerico
return saludoDecorador@saludo("Hola, buenas tardes, ")
def mensaje1(hora):
print("son las " + hora)
mensaje1("3 pm")
@saludo("Hola, buenos días, ")
def mensaje2(hora):
print("son las " + hora)
mensaje2("8 am")
@saludo("καλημερα ")
def mensaje3(hora):
print(" <--- en griego " + hora)
mensaje3(" :D ")Hola, buenas tardes, son las 3 pm
Hola, buenos días, son las 8 am
καλημερα <--- en griego :D