Decoración de funciones en Python usando functools

La decoración de funciones, usando functools, en Python nos permite eliminar código repetitivo que debemos escribir una y otra vez, y también nos permite modificar funciones existentes con unas pocas lineas de código. Como ejemplo, veamos la tubería que escribí hace tiempo atrás:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
import sys, re
 
def grep(expression):
    while True:
        text = (yield)
        if re.search(expression, text):
            print(text)
 
def cat(file, target):
    next(target)
    with open(file, "r") as fh:
        for line in fh.readlines():
            target.send(line.strip())
 
def main(args):
    cat(args[0], grep(args[1]))
 
if __name__ == "__main__":
    main(sys.argv[1:])

En la linea 11 pueden ver que hay que preparar el otro lado de la tubería antes de enviar datos, lo cual es un fastidio; Usando ‘functools’ podemos crear una anotación a la medida, la cual llamaremos ‘pipe’ (lineas 6-12):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3
# Shows how to use a function decorator to create an annotation
# josevnz@kodegeek.com
import sys, re, functools
 
def pipe(function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        target = function(*args, **kwargs)
        next(target) # Saves us the trouble of calling next on the pipe
        return target
    return wrapper
 
@pipe
def grep(expression):
    while True:
        text = (yield)
        if re.search(expression, text):
            print(text)
 
def cat(file, target):
    with open(file, "r") as fh:
        for line in fh.readlines():
            target.send(line.strip())
 
def main(args):
    cat(args[0], grep(args[1]))
 
if __name__ == "__main__":
    main(sys.argv[1:])

En la línea 14 agregamos la nueva anotación y note como eliminamos el ‘next(target)’ completamente de la función cat(file, target). No paree gran cosa, pero es una línea menos de código que poner en cada llamada.

¿Pero se pueden hacer otras cosas? Se me ocurren decoraciones como ‘count’ o ‘sorted’ para funciones que trabajan con listas, estoy aprendiendo como usar esta nueva herramienta, ya les diré como me va.