Decoradores de pitón
Decoradores de Python
Un decorador toma una función, agrega alguna funcionalidad y la devuelve. En este tutorial, aprenderá cómo puede crear un decorador y por qué debería usarlo.
Video:@Decorators en Python
Decoradores en Python
Python tiene una característica interesante llamada decoradores para agregar funcionalidad a un código existente.
Esto también se llama metaprogramación porque una parte del programa intenta modificar otra parte del programa en tiempo de compilación.
Requisitos previos para aprender decoradores
Para entender acerca de los decoradores, primero debemos saber algunas cosas básicas en Python.
Debemos sentirnos cómodos con el hecho de que todo en Python (¡Sí! Incluso las clases) son objetos. Los nombres que definimos son simplemente identificadores vinculados a estos objetos. Las funciones no son una excepción, también son objetos (con atributos). Se pueden vincular varios nombres diferentes al mismo objeto de función.
Aquí hay un ejemplo.
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
Salida
Hello Hello
Cuando ejecuta el código, ambas funciones first
y second
dar la misma salida. Aquí, los nombres first
y second
se refieren al mismo objeto de función.
Ahora las cosas empiezan a ponerse más raras.
Las funciones se pueden pasar como argumentos a otra función.
Si ha utilizado funciones como map
, filter
y reduce
en Python, entonces ya sabes sobre esto.
Las funciones que toman otras funciones como argumentos también se denominan funciones de orden superior. . Aquí hay un ejemplo de tal función.
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
Invocamos la función de la siguiente manera.
>>> operate(inc,3)
4
>>> operate(dec,3)
2
Además, una función puede devolver otra función.
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
# Outputs "Hello"
new()
Salida
Hello
Aquí, is_returned()
es una función anidada que se define y devuelve cada vez que llamamos a is_called()
.
Finalmente, debemos saber sobre Closures en Python.
Volviendo a Decoradores
Las funciones y los métodos se denominan invocables como se les puede llamar.
De hecho, cualquier objeto que implemente el __call__()
especial método se denomina invocable. Entonces, en el sentido más básico, un decorador es un invocable que devuelve un invocable.
Básicamente, un decorador toma una función, agrega alguna funcionalidad y la devuelve.
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
Cuando ejecuta los siguientes códigos en shell,
>>> ordinary()
I am ordinary
>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary
En el ejemplo que se muestra arriba, make_pretty()
es decorador En el paso de asignación:
pretty = make_pretty(ordinary)
La función ordinary()
se decoró y la función devuelta recibió el nombre pretty
.
Podemos ver que la función de decorador agregó algunas funciones nuevas a la función original. Esto es similar a empacar un regalo. El decorador actúa como envoltorio. La naturaleza del objeto que se decoró (regalo real en el interior) no se altera. Pero ahora, se ve bonito (desde que se decoró).
Generalmente, decoramos una función y la reasignamos como,
ordinary = make_pretty(ordinary).
Esta es una construcción común y por esta razón, Python tiene una sintaxis para simplificar esto.
Podemos usar el @
símbolo junto con el nombre de la función decoradora y colóquelo encima de la definición de la función a decorar. Por ejemplo,
@make_pretty
def ordinary():
print("I am ordinary")
es equivalente a
def ordinary():
print("I am ordinary")
ordinary = make_pretty(ordinary)
Esto es solo un azúcar sintáctico para implementar decoradores.
Funciones decorativas con parámetros
El decorador anterior era simple y solo funcionaba con funciones que no tenían ningún parámetro. ¿Qué pasaría si tuviéramos funciones que tomaran parámetros como:
def divide(a, b):
return a/b
Esta función tiene dos parámetros, a y b . Sabemos que dará error si pasamos b como 0.
>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Ahora hagamos que un decorador verifique este caso que causará el error.
def smart_divide(func):
def inner(a, b):
print("I am going to divide", a, "and", b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a, b)
return inner
@smart_divide
def divide(a, b):
print(a/b)
Esta nueva implementación devolverá None
si surge la condición de error.
>>> divide(2,5)
I am going to divide 2 and 5
0.4
>>> divide(2,0)
I am going to divide 2 and 0
Whoops! cannot divide
De esta manera podemos decorar funciones que toman parámetros.
Un observador entusiasta notará que los parámetros del inner()
anidado La función dentro del decorador es la misma que los parámetros de las funciones que decora. Teniendo esto en cuenta, ahora podemos crear decoradores generales que funcionen con cualquier cantidad de parámetros.
En Python, esta magia se hace como function(*args, **kwargs)
. De esta forma, args
será la tupla de argumentos posicionales y kwargs
será el diccionario de argumentos de palabras clave. Un ejemplo de tal decorador será:
def works_for_all(func):
def inner(*args, **kwargs):
print("I can decorate any function")
return func(*args, **kwargs)
return inner
Encadenamiento de decoradores en Python
Se pueden encadenar varios decoradores en Python.
Es decir, una función se puede decorar varias veces con decoradores diferentes (o iguales). Simplemente colocamos los decoradores encima de la función deseada.
def star(func):
def inner(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent
def printer(msg):
print(msg)
printer("Hello")
Salida
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
La sintaxis anterior de,
@star
@percent
def printer(msg):
print(msg)
es equivalente a
def printer(msg):
print(msg)
printer = star(percent(printer))
El orden en el que encadenamos a los decoradores es importante. Si hubiéramos invertido el orden como,
@percent
@star
def printer(msg):
print(msg)
La salida sería:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
python
- Tipos de datos de Python
- Operadores de Python
- Declaración de paso de Python
- Argumentos de la función de Python
- Python Anónimo/Función Lambda
- Funciones Python Lambda con EJEMPLOS
- Función Python abs():Ejemplos de valores absolutos
- Función Python round() con EJEMPLOS
- Python range() Función:Flotante, Lista, Para ejemplos de bucle
- Función Python map() con EJEMPLOS
- Tutorial de Rendimiento en Python:Generador y Rendimiento vs Ejemplo de Retorno