Un decorador es una herramienta de patrón de diseño en Python para envolver código alrededor de funciones o clases (bloques definidos). Este patrón de diseño permite a un programador agregar nueva funcionalidad a funciones o clases existentes sin modificar la estructura existente. La sección proporciona una descripción general de qué son los decoradores, cómo decorar funciones y clases, y qué problema puede resolver.
Comprender a los decoradores
Un decorador es una función que toma otra función como argumento, realiza algunas acciones y luego devuelve el argumento en función de las acciones realizadas. Dado que las funciones son objetos de primera clase en Python, se pueden pasar como argumentos a otras funciones.
Por lo tanto, podemos decir que un decorador es un invocable que acepta y devuelve un invocable.
El siguiente código muestra un decorador simple que agrega notas adicionales a la string de documentación my_function :
def decorated_docstring(function): function.__doc__ += '\n Hi George, I''m a simple Decorator.' return function def my_function(string1): """Return the string.""" return string1 def main(): myFunc = decorated_docstring(my_function) myFunc('Hai') help(myFunc) if __name__ == "__main__": main()
Producción:
Help on function my_function in module __main__: my_function(string1) Return the string. Hi George, Im a simple Decorator.
Sintaxis del decorador
De la explicación anterior, puedes entender cómo decorar una función. Pero es inaceptable definir una función, asignarla a una variable y luego reasignar la función decorada a la misma variable. Por lo tanto, Python 2.5 introdujo una sintaxis para la función de decoración al anteponer un @
carácter con el nombre del decorador y agregarlo directamente encima de la declaración de la función. El siguiente código muestra cómo usar la sintaxis:
def reverse_decorator(function): def reverse_wrapper(): make_reverse = "".join(reversed(function())) return make_reverse return reverse_wrapper @reverse_decorator def say_hi(): return 'Hi George' def main(): print(say_hi()) if __name__ == "__main__": main()
Producción:
egroeG iH
Aquí usamos el carácter @ para la función de decoración.
@reverse_decorator def say_hi(): return 'Hi George'
El código anterior es sintácticamente equivalente al siguiente códigoreverse_decorator(say_hi())
En este caso, la función reverse_decorator se ejecuta y crea la referencia para revserse_wrapper. Veamos el siguiente ejemplo para una mejor comprensión:
def reverse_decorator(function): print('Inside reverse_decorator function') def reverse_wrapper(): print('Inside reverse_wrapper function') return 'Return reverse_wrapper function' return reverse_wrapper @reverse_decorator def say_hi(): return 'Inside say_hi'
Producción:
Inside reverse_decorator function
Aquí, reverse_decorator no ejecuta la función reverse_wrapper, sino que crea la referencia y la devuelve cuando el invocable invoca la función.
def reverse_decorator(function): print('Inside reverse_decorator function') def reverse_wrapper(): print('Inside reverse_wrapper function') return 'reverse_wrapper' return reverse_wrapper @reverse_decorator def say_hi(): return 'Inside say_hi' def main(): print('Inside main()') print(say_hi) print(''); print(say_hi()) # main executes the reference main()
Producción:
Dentro de la función reverse_decorator
Dentro de main()
<función reverse_decorator..reverse_wrapper en 0x0000015762A16708>Dentro de la función
reverse_wrapper reverse_wrapper
Función única y decorador múltiple
Ahora tienes claro cómo usar la @syntax para decorar una función. Otra cosa interesante es que podemos usar múltiples decoradores en una sola función. Una cosa importante a tener en cuenta aquí es el orden del decorador aplicado, que se aplica de abajo hacia arriba . Veamos varios decoradores.
def reverse_decorator(function): def reverse_wrapper(): make_reverse = "".join(reversed(function())) return make_reverse return reverse_wrapper def uppercase_decorator(function): def uppercase_wrapper(): var_uppercase = function().upper() return var_uppercase return uppercase_wrapper @uppercase_decorator @reverse_decorator def say_hi(): return 'hi george' def main(): print(say_hi()) if __name__ == "__main__": main()
Producción:
EGROEG IH
Aquí, la string se invierte en primer lugar y, en segundo lugar, se convierte a mayúsculas.
@uppercase_decorator @reverse_decorator def say_hi(): return 'hi george'
El código anterior es sintácticamente equivalente auppercase_decorator(reverse_decorator(say_hi()))
Argumentos en la función de decorador
Hasta ahora, has visto decoradores sin ningún argumento. En ciertos casos, es necesario pasar argumentos para decorar el método en consecuencia.
def decorator_arguments(function): def wrapper_arguments(arg1, arg2): print("Arguments accepted are: {0}, {1}".format(arg1, arg2)) function(arg1, arg2) # calls the function hobbies return wrapper_arguments @decorator_arguments def hobbies(hobby_one, hobby_two): print("My Hobbies are {0} and {1}".format(hobby_one, hobby_two)) def main(): hobbies("Travelling", "Reading") if __name__ == "__main__": main()
Producción:
Los argumentos aceptados son: Viajar, Leer
Mis pasatiempos son Viajar y Leer
En el caso anterior, el decorador no tomará ningún argumento, sino que el invocable lo pasará a la función contenedora. El siguiente código proporciona más claridad en el proceso de pasar los argumentos mediante referencia.
def decorator_arguments(function): def wrapper_arguments(arg1, arg2): print(" Arguments accepted are: {0}, {1}".format(arg1, arg2)) return wrapper_arguments @decorator_arguments def hobbies(hobby_one, hobby_two): print("My Hobbies are {0} and {1}".format(hobby_one, hobby_two)) def main(): # the reference of wrapper_arguments # is returned hob = hobbies print(hob) # passing the arguments to the # wrapper_arguments hob('Travelling', 'Reading') main()
Producción
<function decorator_arguments..wrapper_arguments at 0x7f100315e158> Los
argumentos aceptados son: viajar, leer
¿Dónde se utilizan los decoradores?
The standard library provides many modules that include decorators and many Python tools and frameworks that make use of decorators. Few examples are:
- Puede usar el decorador @classmethod o @staticmethod para crear un método sin crear una instancia
- El módulo simulado permite el uso de @mock.patch o @mock.patch.object como decorador que se usa para pruebas unitarias.
- Las herramientas comunes como Django (usa @login_required) para configurar los privilegios de inicio de sesión y Flask (usa @app.route) para el registro de funciones usa decoradores.
- Para identificar una función como una tarea asíncrona, Celery usa el decorador @task.
Por qué deberías escribir Decoradores
Two important reasons to write this reusable piece of Python functionality are when written properly the decorators are modular and explicit.
- Modularidad de Decoradores
- Los decoradores son explícitos
Modularidad de Decoradores
El uso de programadores de decoradores puede agregar o eliminar funcionalidades fácilmente en bloques de código definidos, lo que evita la repetición de la configuración repetitiva.
Los decoradores son explícitos
Los programadores pueden aplicar decoradores a todos los llamables según la necesidad. Esto asegura la legibilidad y proporciona un código limpio.
Casos de uso del decorador
Here we will discuss a few decorator use-cases which help you to understand When to write Decorators.
- Complementos funcionales
- Sanitización de datos
- Registro de función
Complementos funcionales
La primera y principal razón para escribir un decorador es su flexibilidad para agregar funcionalidad adicional a los bloques de código definidos (funciones y clases).
Sanitización de datos
Con los métodos adecuados de desinfección de datos, puede eliminar o destruir los datos almacenados en la memoria y hacer que sea imposible recuperarlos. Por ejemplo, puede usar la función de caché para evitar ejecutar la misma función o puede usar métodos para validar las credenciales de inicio de sesión del usuario, etc., lo que indica la importancia de la desinfección de datos. Un decorador puede desinfectar adecuadamente los argumentos que pasaron a la función decorada y también desinfectar los datos devueltos por la función.
Registro de función
Otro punto para escribir un decorador es registrar una función. Aquí el decorador permite que dos o más subsistemas se comuniquen sin tener mucha información entre sí.
Clases de decoración
La sección anterior muestra cómo los decoradores ayudan en las funciones de decoración. Hemos visto que los decoradores son invocables que aceptan y devuelven invocables. Dado que las clases son invocables, los decoradores también se utilizan para decorar clases. Algunos de los usos de las clases de decoración son:
- Adición o mejora de atributos
- Interacción de atributos
- Alterar la API de una clase (alterar la forma de declarar clases y su uso de instancia)
Veamos cómo decorar una función usando class.
class MyDecorator: def __init__(self, function): self.function = function def __call__(self): print("Inside Function Call") self.function() @MyDecorator def function(): print("GeeksforGeeks") def main(): function() if __name__ == "__main__": main()
Producción:
Inside Function Call GeeksforGeeks
Veamos otro ejemplo. Debajo del código, ordene la instancia según su tiempo de creación. Aquí requerimos tres atributos adicionales: la marca de tiempo de creación de instancias, los métodos __lt__ y __gt__.
import functools import time def sort_by_creation_time(cl): org_init = cl.__init__ # Enhance the class to store the creation # time based on the instantiation. @functools.wraps(org_init) def new_init(self, *args, **kwargs): org_init(self, *args, **kwargs) self._created = time.time() # __lt__ and __gt__ methods return true or false # based on the creation time. cl.__init__ = new_init cl.__lt = lambda self, other: self._created < other._created cl.__gt__ = lambda self, other: self._created > other._created return cl @sort_by_creation_time class Sort(object): def __init__(self, identifier): self.identifier = identifier def __repr__(self): return self.identifier def main(): first = Sort('Python') second = Sort('Data Analysis') third = Sort('Machine Learning') sortables = [third, first, second] print(sorted(sortables)) if __name__ == "__main__": main()
Producción
[Python, Data Analysis, Machine Learning]
new_init
, su principal responsabilidad es ejecutar la función envuelta y también agregar una funcionalidad adicional a la función envuelta . La @functools.wraps(org_init)
actualización de la función contenedora para reflejar el aspecto de la función envuelta. Verifique las funciones para comprender los detalles.
@functools.wraps(org_init) def new_init(self, *args, **kwargs): # calls the init method of class org_init(self, *args, **kwargs) # add an extra attribute and store # creation time self._created = time.time() # reference of new_init is assigned to # __init__ method and executes when # callable creates the class object. cl.__init__ = new_init
Decora una función y devuelve una clase
A veces es necesario decorar una función y devolver una clase. Digamos, por ejemplo, que los desarrolladores de casos avanzados pueden subclasificar una clase en una API. También puede evitar un aumento en el código repetitivo.
class Addition(object): def __call__(self, *args, **kwargs): return self.run(*args, **kwargs) def run(self, *args, **kwargs): raise NotImplementedError('Subclass must implement `run`.') def identity(self): return 'Hi George, I''m here to help you with addition !' def addition(decorated): class AddSubclass(Addition): def run(self, *args, **kwargs): if args: add = 0 for arg in args: add = arg + add # add arguments return add else: # func is called if there are no arguments passed. return decorated(*args, **kwargs) return AddSubclass() @addition def func(*args): return 1 + 1 print(func.identity()) print(func()) print(func(2, 2)) print(func(2, 1, 4))
Producción
Hi George, Im here to help you with addition! 2 4 7
Aquí el decorador crea una subclase de Adición y devuelve la instancia de la subclase en lugar de la referencia ( return AddSubclass()
).
def addition(decorated): class AddSubclass(Addition): def run(self, *args, **kwargs): if args: add = 0 for arg in args: add = arg + add return add else: return decorated(*args, **kwargs) return AddSubclass()
Y el método __call__ en la clase `Addition` significa que su instancia es invocable. Por lo tanto, la instancia de la subclase `AddSubclass()` puede ejecutar la tarea y, como resultado, el programador puede evitar llamar al método de ejecución como func().run()
.
Resumen
Los decoradores son una excelente herramienta para envolver código alrededor de funciones y clases, lo que proporciona una manera eficiente de usar código repetitivo y también contribuye a la legibilidad. Aunque son modulares y explícitos, esta herramienta tiene inconvenientes. Dado que es una herramienta para envolver código alrededor de bloques definidos, la depuración se vuelve más desafiante. Y también, los decoradores mal escritos pueden resultar en un error.
Después de todo, un decorador bien escrito puede tratarse como una pieza eficiente y reutilizable de la funcionalidad de Python para envolver bloques de código definidos.
Publicación traducida automáticamente
Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA