Los Python de­co­ra­to­rs o decorador de Python son una forma de extender la fu­n­cio­na­li­dad básica de una función sin cambiar su código fuente su­b­ya­ce­n­te.

¿Qué son los de­co­ra­do­res y para qué sirven?

El uso de Python de­co­ra­to­rs no se explica en detalle en muchos tu­to­ria­les de Python. La razón es que para entender los de­co­ra­do­res es necesario primero estar bien fa­mi­lia­ri­za­do con las propias funciones. Los de­co­ra­do­res de Python pueden re­co­no­ce­r­se en el código por su operador de Python, esto es, el signo “@” seguido del nombre del decorador de la función.

Nota

Puedes aprender más sobre co­n­s­tru­c­cio­nes avanzadas de pro­gra­ma­ción en Python en los artículos:

Puedes ver las sintaxis básicas de una llamada en un Python decorator en el siguiente ejemplo de código, que, sin embargo, no im­ple­me­n­ta ninguna fu­n­cio­na­li­dad:

@decorador
def función():
 pass
Python

El código al­ma­ce­na­do en “decorador” se eje­cu­ta­ría en este ejemplo cuando se llame a la función llamada “función”.

Los de­co­ra­do­res se utilizan a menudo en la pro­gra­ma­ción orientada a objetos con Python. Por ejemplo, existe el decorador Python property. Se utiliza como equi­va­le­n­te a los métodos getter y setter en otros lenguajes de pro­gra­ma­ción.

Consejo

Python es un lenguaje de pro­gra­ma­ción ideal para proyectos web, gracias, por ejemplo, a co­n­s­tru­c­cio­nes prácticas como los de­co­ra­do­res. También es excelente para proyectos web IONOS Deploy Now. Con esta he­rra­mie­n­ta puedes de­sa­rro­llar y construir fá­ci­l­me­n­te tu proyecto a través de GitHub y hacer un se­gui­mie­n­to.

Uso de los de­co­ra­do­res de Python

Am­plia­ción de las funciones básicas mediante de­co­ra­do­res de Python

Los de­co­ra­do­res de Python se utilizan en la mayoría de los casos para ampliar la fu­n­cio­na­li­dad básica de una función. Esto es práctico si utilizas la misma función básica en varios casos de uso y quieres ex­te­n­de­r­la in­di­vi­dua­l­me­n­te. Un ejemplo de código sencillo, pero ya eje­cu­ta­ble, ilustra el de los de­co­ra­do­res para el fin descrito:

def dec(función):
 def foo(x):
  print("Antes de llamar a la función " + función.__nombre__)
  función(x)
       print("Después de llamar a la función " + función.__nombre__)
 return foo
@dec
def bar(y):
 print("Llamada a la función bar con el valor " + str(y))
bar("Test")
Python

En el código de ejemplo anterior, primero se crea el decorador “dec”, que a su vez contiene una función llamada “foo”. Como puedes ver, este decorador bá­si­ca­me­n­te no es más que una función wrapper in­de­pe­n­die­n­te, que en este caso contiene otra función llamada “foo”. “Foo2” muestra primero que te en­cue­n­tras ante la llamada de la función pasada al decorador, después se ejecuta la función con el parámetro y, fi­na­l­me­n­te, se ejecuta una llamada más de python-print, que indica que estás después de la llamada de la función pasada al decorador.

La segunda parte del código consiste en la de­fi­ni­ción de una función llamada “bar”, que toma un parámetro de paso llamado “y”. La fu­n­cio­na­li­dad de bar es fácil de entender: muestra en pantalla la frase “Llamada a la función de bar con el valor y”, uti­li­za­n­do para y el valor pasado como parámetro. Lo especial de la función bar es que ha sido decorada. Esto se puede ver en el ejemplo de código en la línea “@dec” antes de la de­fi­ni­ción de la función.

Pero ¿qué significa exac­ta­me­n­te que la función ha sido decorada? Su­po­n­ga­mos que se hubiera omitido el Python decorator, es decir, la línea de código “@dec”. La llamada a la función “bar”, con la que concluye el ejemplo de código, pro­po­r­cio­na­ría entonces el siguiente resultado:

Llamada a la función bar con el valor Test
Python

Lo que ocurre aquí es exac­ta­me­n­te lo que se espera de la llamada a la función: la cadena Python “Test” pasada como parámetro se introduce en la sentencia “print”. La función devuelve el resultado visto en el ejemplo anterior.

Ahora observa el resultado de la misma llamada a “bar”, esta vez decorando la función “bar” con el Python decorator:

Antes de llamar a la función bar
Llamada a la función bar con el valor Test
Después de llamar a la función bar

Lo que ves tal vez te sorprenda: después de que la función haya sido decorada, no solo muestra el resultado de su propia sentencia “print” en la pantalla. El resultado de la función de­co­ra­do­ra se ha en­ca­p­su­la­do de forma que ahora también se incluyen las dos se­n­te­n­cias “print” de la función auxiliar “foo”. La fu­n­cio­na­li­dad principal de “bar” se ha ampliado añadiendo dos se­n­te­n­cias “print” adi­cio­na­les mediante el uso del Python decorator.

Por supuesto, este ejemplo es ar­ti­fi­cial y no sigue ninguna lógica de pro­gra­ma­ción. Sin embargo, es su­fi­cie­n­te para ofrecer una visión general del fu­n­cio­na­mie­n­to de los Python de­co­ra­to­rs. Por supuesto, puedes incluir cualquier fu­n­cio­na­li­dad de Python en la función del decorador.

Consulta de co­n­di­cio­nes re­cu­rre­n­tes con Python de­co­ra­to­rs

Es posible que desees ejecutar ciertas funciones úni­ca­me­n­te cuando se cumpla una condición de­te­r­mi­na­da. Para ello, pro­ba­ble­me­n­te ya estés fa­mi­lia­ri­za­do con las funciones if else de Python. Sin embargo, si estas co­n­di­cio­nes deben co­m­pro­bar­se en di­fe­re­n­tes si­tua­cio­nes, puede ser útil para la claridad de tu código en­ca­p­su­lar la condición en un Python decorator.

De nuevo, un ejemplo de código ayuda a vi­sua­li­zar el uso del decorador. Algunos ope­ra­do­res ma­te­má­ti­cos solo están definidos para los números naturales. Por lo tanto, sería útil tener una función de­co­ra­do­ra que compruebe si el parámetro de paso de una función es un número natural:

def número_natural(función):
 def test(x):
  if type(x) == int and x > 0:
   return función(x)
  else:
   raise Exception("El argumento no es un número natural")
@número_natural
def fac(n):
 if n == 1:
  return 1
 else:
   return n * fac(n-1)
print(fac(5))
print(fac(-1))
Python

En el código anterior, primero has de definir el Python decorator, llamado “número_natural”, que comprueba si el argumento de la función que se le pasa llamado “función” es un número natural. Para ello, primero se comprueba el tipo del argumento en la condición if. También se comprueba si el argumento es un número positivo mayor que 0. Si es así, se ejecuta la función pasada al decorador como parámetro. En caso contrario, se lanza una excepción, in­fo­r­ma­n­do de que el argumento de la función no es un número natural.

La forma en que funciona el Python decorator puede ilu­s­trar­se en la práctica ob­se­r­va­n­do la función llamada “fac” que está decorada con dicho decorador. Se define en el código y se llama primero con el valor “5” y luego con el valor “-1”. El resultado es el siguiente:

120
Traceback (most recent call last):
    File "<pyshell#17>", line 1, in <module>
        fac(-1)
    File "<pyshell#11>", line 6, in test
        raise Exception("El argumento no es un número natural ")
Exception: El argumento no es un número natural

Primero tienes el número “120”, que co­rre­s­po­n­de al factorial de 5. Así que, para números naturales, la función “fac” funciona. Sin embargo, llamar a la función “fac” con un número negativo conduce a un error y eso se debe al Python decorator. Dado que un número negativo no es un número natural, la función factorial “fac” no debe eje­cu­tar­se.

Ir al menú principal