En Python las funciones también se consideran objetos y como consecuencia de esto se pueden asignar a una variable, almacenarlas en estructuras de datos (listas, tuplas, diccionarios...) o incluso pasarlas como argumento de otras funciones.
deffunc():print("Hola mundo")func()Hola mundofunc<function __main__.func()>var = funcvar()Hola mundodeffunc2(funcion):funcion()func2(func)Hola mundo
2. ¿Qué es un decorator?
Los decorator envuelven una función, modificando su comportamiento realizando una combinación de todas las propiedades que hemos visto anteriormente.
defmi_funcion():print("Hola mundo")# Añadimos nuevas funcionalidades a una función ya existentedefmi_decorator(func):defwrapper():print("Ejecución antes de la llamada a la funcion")func()print("Ejecución después de la llamada a la funcion")return wrappermi_funcion_mod =mi_decorator(mi_funcion)mi_funcion_mod()Ejecución antes de la llamada a la funcionHola mundoEjecución después de la llamada a la funcion
Podemos utilizar decorators para modificar el comportamiento de una función que programemos, por ejemplo, añadiendo condiciones que se evalúen antes de ejecutar la función ya existente que no queremos modificar.
deffuncion():print("Hola mundo")defmi_decorator(func):defwrapper():if var <5:func()else:print("No se puede ejecutar la funcion")return wrapperfuncion =mi_decorator(funcion)var =2funcion()Hola mundovar =10funcion()No se puede ejecutar la funcion
3. Syntactic Sugar
La sintaxis que hemos utilizado en el apartado anterior para definir el decorator es bastante compleja, por ello, Python nos proporciona una alternativa mucho más sencilla.
defmi_decorator(func):defwrapper():print("Ejecución antes de la llamada a la funcion")func()print("Ejecución después de la llamada a la funcion")return wrapper# Añadimos @ y el decorador a aplicar a una función @mi_decoratordeffuncion():print("Hola mundo")funcion()Ejecución antes de la llamada a la funcionHola mundoEjecución después de la llamada a la funcion
4. Decorators en las Clases
Una de las cosas interesantes sobre los decorators es que Python nos proporciona varios definidos por defecto que podemos utilizar dentro de una clase.
Uno de los decorators más interesantes que podemos utilizar es @property, que nos permite definir métodos en una clase para consultar y modificar un atributo interno.
# Clase modificadaclassCoche():"""Esta clase representa un coche."""def__init__(self,modelo,potencia,consumo):"""Inicializa los atributos de instancia. Argumentos posicionales: modelo -- string que representa el modelo del coche potencia -- int que representa la potencia en cv conumo -- int que representa el consumo en l/100km """ self._modelo = modelo self._potencia = potencia self._consumo = consumo self._km_actuales =0defespecificaciones(self):"""Muestra las especicificaciones del coche."""print("Modelo:", self._modelo,"\nPotencia: {} cv".format(self._potencia),"\nConsumo: {} l/100km".format(self._consumo),"\nKilometros actuales:", self._km_actuales)@propertydefkilometros(self):return self._km_actuales@kilometros.setterdefkilometros(self,kilometros):"""Actualiza los kilometros del coche."""if kilometros > self._km_actuales: self._km_actuales = kilometroselse:print("ERROR: No se puede establecer un numero de kilometros inferior al actual")defconsumo_total(self):"""Muestra el consumo total del coche desde el kilometro 0.""" consumo_total = (self._km_actuales /100) * self._consumoprint("El consumo total es de {} litros".format(consumo_total))
bmw =Coche("bmw i3", 150, 6)# Esto es una mala prácticabmw._km_actuales0# Buena prácticabmw.kilometros0bmw.kilometros =500bmw.especificaciones()Modelo: bmw i3 Potencia:150 cv Consumo:6 l/100km Kilometros actuales:500bmw.kilometros500bmw.kilometros =200ERROR: No se puede establecer un numero de kilometros inferior al actual