Cómo entender a los decoradores de Python
Un decorador es esencialmente una función de Python, que permite que otras funciones agreguen funciones adicionales sin realizar ningún cambio en el código. El valor de retorno del decorador también es un objeto de función. A menudo se utiliza en escenarios con requisitos transversales, como: inserción de registros, pruebas de rendimiento, procesamiento de transacciones, almacenamiento en caché, verificación de permisos, etc. Los decoradores son un diseño excelente para resolver este tipo de problemas. Con los decoradores, podemos extraer una gran cantidad de código similar que no tiene nada que ver con la función en sí y continuar reutilizándolo. En pocas palabras, el propósito de un decorador es agregar funcionalidad adicional a un objeto existente.
Veamos primero un ejemplo sencillo:
def foo():
print('soy foo')
Ahora Tenemos un nuevo requisito: registrar el registro de ejecución de la función, por lo que agrego el código de registro en el código:
def foo():
print('i am foo ')
logging.info("foo se está ejecutando")
bar() y bar2() también tienen requisitos similares. ¿Escribir otro registro en la función de barra? Esto da como resultado una gran cantidad de código similar. Para reducir la escritura repetida de código, podemos redefinir una función: procesar registros específicamente y luego ejecutar el código comercial real después de procesar los registros
def use_logging(. func):
logging.warn("s se está ejecutando" func.__name__)
func()
def bar():
print('soy bar')
use_logging(bar)
La lógica no es difícil de entender,
Pero en este caso, tenemos para convertir uno La función se pasa como parámetro a la función use_logging. Además, este método ha destruido la estructura lógica del código original. Cuando se ejecutaba la lógica empresarial antes, se ejecutaba bar(), pero ahora debe cambiarse a use_logging(bar). Entonces, ¿hay una mejor manera? Por supuesto que sí, la respuesta son los decoradores.
Decorador simple
def use_logging(func):
def wrapper(*args, **kwargs):
advertencia. ("s se está ejecutando" func.__name__)
return func(*args, **kwargs)
return wrapper
def bar():< / p>
print('soy bar')
bar = use_logging(bar)
bar()
La función use_logging es el decorador. Envuelve la función que ejecuta el método comercial real en la función y parece que la barra está decorada con use_logging. En este ejemplo, cuando la función entra y sale
, se llama aspecto (Aspecto), y este método de programación se llama programación orientada a aspectos (Programación orientada a aspectos).
El símbolo @ es el azúcar sintáctico del decorador. Se utiliza al definir una función para evitar otra operación de asignación.
def use_logging(func):
def wrapper (*args, **kwargs):
logging.warn("s is running" func.__name__)
return func(*args)
envoltura de retorno
@use_logging
def foo():
print("soy foo")
@use_logging p>
def bar():
print("soy bar")
bar()
Como se muestra arriba, podemos omitir bar =
use_logging(bar) es la oración. Puede obtener el resultado deseado llamando a bar() directamente. Si tenemos otras funciones similares, podemos continuar llamando al decorador para decorar la función sin modificar la función repetidamente o agregar nuevos paquetes. De esta forma, mejoramos la reutilización del programa y aumentamos la legibilidad del programa.
La razón por la que los decoradores son tan convenientes de usar en Python es que las funciones de Python se pueden pasar como parámetros a otras funciones como objetos ordinarios, se pueden asignar a otras variables, se pueden usar como valores de retorno y se pueden definirse en otra función.
Decoradores con parámetros
Los decoradores también tienen mayor flexibilidad, como los decoradores con parámetros: En la llamada al decorador anterior, como @use_logging, el decorador El único parámetro del controlador es el Función que realiza el negocio. La sintaxis de los decoradores nos permite proporcionar otros parámetros al llamar, como @decorator(a). Esto proporciona una mayor flexibilidad al escribir y utilizar decoradores.
def use_logging(nivel):
def decorador(func):
def wrapper(*args, **kwargs):
if nivel == "warn":
logging.warn("s is running" func.__name__)
return func(*args)
return envoltorio
decorador de retorno
@use_logging(level="warn")
def foo(name='foo'):
print("soy el nombre")
foo()
El use_logging anterior es un decorador que permite parámetros. En realidad, es una función que encapsula el decorador original y devuelve un decorador. Podemos entenderlo como un cierre que contiene parámetros. Cuando llamamos a @use_logging(level="warn"), Python puede descubrir esta capa de encapsulación y pasar los parámetros al entorno decorador.
Decoradores de clases
Echemos un vistazo a los decoradores de clases. En comparación con los decoradores de funciones, los decoradores de clases tienen las ventajas de una mayor flexibilidad, alta cohesión y encapsulación.
El uso de decoradores de clases también puede depender del método \_\_call\_\_ dentro de la clase, que se llama cuando el decorador está adjunto a una función usando el formulario @.
clase Foo(objeto):
def __init__(self, func):
self._func = func
def __call__( self):
print ('decorador de clase ejecutándose')
self._func()
print ('decorador de clase finalizando')
@Foo
def bar():
print ('bar')
bar()
functools.wraps
El uso de decoradores reutiliza en gran medida el código, pero una desventaja es que falta la metainformación de la función original, como la cadena de documentación, __nombre__ y la lista de parámetros de la función. Veamos primero el ejemplo:
Decorador
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ " fue llamado "
return func(*args, **kwargs)
return with_logging
función
@logged
def f(x):
"""hace algunos cálculos"""
return x x * x
Esta función es equivalente a:
def f(x):
"""hace algunos cálculos"""
return x x * x
f = logged(f)
No es difícil encontrar que la función f ha sido reemplazada por with_logging. Por supuesto, su cadena de documentación y __name__ se han convertido en la información de la función with_logging.
print f.__name__ # imprime 'with_logging'
print f.__doc__ # imprime Ninguno
Este problema es más grave, tenemos functools. , wraps en sí también es un decorador, puede copiar la metainformación de la función original en la función decoradora, lo que hace que la función decoradora tenga la misma metainformación que la función original.
desde functools importar envolturas
def logged(func):
@wraps(func)
def with_logging(*args, * *kwargs):
print func.__name__ "fue llamado"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""hace algunos cálculos"""
devuelve x x * x
print f.__name__ # imprime 'f'
print f.__doc__ # imprime 'hace algunos cálculos'
Decoradores integrados
@staticmathod, @ método de clase, @property
El orden de los decoradores
@a
@b
@c
def f():
Equivalente a
f = a(b(c(f)))