Seis formas de escribir decoradores de Python

Prefacio

El texto y las imágenes de este artículo son de Internet y son únicamente con fines de aprendizaje y comunicación. No tienen ningún uso comercial. Si tiene alguna pregunta, comuníquese con nosotros para su procesamiento.

PD: Si necesita materiales de aprendizaje de Python, puede hacer clic en el enlace de abajo para obtenerlo usted mismo.

Materiales de aprendizaje gratuitos de Python y respuestas de comunicación grupal Haga clic para unirse


Hoy compartiré contigo los puntos de conocimiento sobre decoradores. El contenido es muy seco y lleno de energía. Después de absorber y leer cuidadosamente, definitivamente tendrás una comprensión más profunda de los decoradores.

Hola decorador

El decorador es esencialmente una función de Python, que permite que otras funciones agreguen funciones adicionales sin ningún cambio de código. El valor de retorno del decorador también es un objeto de función.

A menudo se usa en escenarios con requisitos de aspectos, como la inserción de registros, pruebas de rendimiento, procesamiento de transacciones, almacenamiento en caché y verificación de permisos.

Decorator es un diseño excelente para resolver este tipo de problemas, con Decorator podemos extraer mucho código similar que no tiene nada que ver con la función en sí y seguir reutilizándolo.

El uso de decoradores es muy fijo

  • Primero defina un decorador (sombrero)
  • Luego defina su función comercial o clase (persona)
  • Finalmente pon el decorador (sombrero) en la cabeza de esta función (persona)

Como abajo

# 定义装饰器
def decorator(func):
    def wrapper(*args, **kw):
        return func()
    return wrapper

# 定义业务函数并进行装饰
@decorator
def function():
    print("hello, decorator")

De hecho, los decoradores no son necesarios para codificar, lo que significa que puedes usar decoradores sin ellos. Su apariencia debería hacer que nuestro código

  • Estructura de código más elegante y clara
  • Encapsule códigos de función específicos en decoradores para mejorar la tasa de reutilización del código y mejorar la legibilidad del código

A continuación, explicaré con ejemplos cómo escribir una variedad de decoradores simples y complejos.

El primer tipo: decorador ordinario

Primero que nada, escribamos un decorador más común, su función es:

  • Antes de ejecutar la función, registre una línea de registro
  • Después de ejecutar la función, registre una línea de registro
# 这是装饰器函数,参数 func 是被装饰的函数
def logger(func):
    def wrapper(*args, **kw):
        print('我准备开始执行:{} 函数了:'.format(func.__name__))

        # 真正执行的是这行。
        func(*args, **kw)

        print('主人,我执行完啦。')
    return wrapper

Supongamos que mi función comercial es calcular la suma de dos números. Después de escribirlo, ponle un sombrero.

@logger
def add(x, y):
    print('{} + {} = {}'.format(x, y, x+y))

Luego ejecute la función agregar.

add(200, 50)

Veamos qué es la salida.

我准备开始执行:add 函数了:
200 + 50 = 250
我执行完啦。

El segundo tipo: decorador de funciones con parámetros

A través de los dos simples ejemplos introductorios anteriores, debería poder apreciar el principio de funcionamiento del decorador.

Sin embargo, el uso de decoradores es mucho más que eso, y aún quedan muchos artículos por estudiar. Aprendamos juntos este punto de conocimiento hoy.

Mirando hacia atrás en el ejemplo anterior, el decorador no puede recibir parámetros. Su uso solo se puede aplicar a algunos escenarios simples. Un decorador que no pasa parámetros solo puede realizar una lógica fija en la función decorada.

El decorador en sí es una función.Como función, si no puede pasar parámetros, entonces la función de esta función será muy limitada y solo podrá ejecutar lógica fija. Esto significa que si la ejecución del código lógico del decorador necesita ajustarse según diferentes escenarios, si los parámetros no se pueden pasar, tenemos que escribir dos decoradores, lo que obviamente no es razonable.

Por ejemplo, si queremos implementar una tarea que pueda enviar correos electrónicos con regularidad (enviar uno por minuto) y tareas de sincronización de tiempo (sincronizar una vez al día), podemos implementar un decorador periodic_task (tarea temporizada) por nosotros mismos, que puede recibir un tiempo parámetro de intervalo, con qué frecuencia se ejecutará la tarea una vez.

Puede escribirlo de la siguiente manera: dado que este código de función es más complicado y no facilita el aprendizaje, no se publicará aquí.

@periodic_task(spacing=60)
def send_mail():
     pass

@periodic_task(spacing=86400)
def ntp()
    pass 

Luego creemos una pseudoescena nosotros mismos, podemos pasar un parámetro en el decorador para indicar la nacionalidad, y antes de que se ejecute la función podemos saludar en nuestro idioma nativo.

# 小明,中国人
@say_hello("china")
def xiaoming():
    pass

# jack,美国人
@say_hello("america")
def jack():
    pass

¿Qué pasa si implementamos este decorador para que pueda pasar parámetros?

Será más complicado y requiere dos niveles de anidación.

def say_hello(contry):
    def wrapper(func):
        def deco(*args, **kwargs):
            if contry == "china":
                print("你好!")
            elif contry == "america":
                print('hello.')
            else:
                return

            # 真正执行函数的地方
            func(*args, **kwargs)
        return deco
    return wrapper

Vamos a ejecutar

xiaoming()
print("------------")
jack()

Mira el resultado.

你好!
------------
hello.

El tercer tipo: decorador de clases sin parámetros

Los anteriores son todos decoradores basados ​​en funciones. Al leer el código de otras personas, a menudo puede encontrar decoradores basados ​​en clases.

Según la implementación del decorador de clases, se deben implementar  dos funciones integradas call  e  init .
__ init __  : recibe la función decorada
__ llama __  : implementa la lógica de decoración.

Tome el ejemplo simple de la impresión de registros como ejemplo

class logger(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("[INFO]: the function {func}() is running..."\
            .format(func=self.func.__name__))
        return self.func(*args, **kwargs)

@logger
def say(something):
    print("say {}!".format(something))

say("hello")

Ejecutarlo y ver el resultado

[INFO]: the function say() is running...
say hello!

El cuarto tipo: decorador de clases con parámetros

En el ejemplo anterior sin parámetros, encuentra que no hay ninguno. Solo se pueden imprimir registros de nivel INFO. En circunstancias normales, también necesitamos imprimir registros de ADVERTENCIA DE DEPURACIÓN. Esto requiere pasar parámetros al decorador de clases y asignar el nivel a esta función.

Hay una gran diferencia entre los decoradores de clases con y sin parámetros.

__init__  : Ya no recibe la función decorada, sino que recibe los parámetros entrantes.
__ llamar __  : Recibe la función decorada e implementa la lógica de decoración.

class logger(object):
    def __init__(self, level='INFO'):
        self.level = level

    def __call__(self, func): # 接受函数
        def wrapper(*args, **kwargs):
            print("[{level}]: the function {func}() is running..."\
                .format(level=self.level, func=func.__name__))
            func(*args, **kwargs)
        return wrapper  #返回函数

@logger(level='WARNING')
def say(something):
    print("say {}!".format(something))

say("hello")

Especificamos el nivel de ADVERTENCIA, lo ejecutamos y vemos el resultado.

[WARNING]: the function say() is running...
say hello!

Quinto: use funciones y clases parciales para implementar decoradores

La mayoría de los decoradores se implementan en función de funciones y cierres, pero esta no es la única forma de hacer decoradores.

De hecho, Python solo tiene un requisito para saber si un objeto se puede usar en forma de decorador (@decorador): el decorador debe ser un objeto "invocable".

Para este objeto invocable, estamos más familiarizados con la función.

Además de las funciones, una clase también puede ser un objeto invocable, siempre que implemente la función __ call __  (ya he tocado los pocos ejemplos anteriores).

Las funciones parciales que se pasan por alto fácilmente son en realidad objetos invocables.

A continuación, hablemos sobre cómo usar una combinación de clases y funciones parciales para implementar un decorador distintivo.

Como se muestra a continuación, DelayFunc es una  clase que implementa  __ call __ . Delay devuelve una función parcial, donde delay puede usarse como decorador. (El siguiente código está tomado de Python Craftsman: consejos para usar decoradores)

import time
import functools

class DelayFunc:
    def __init__(self,  duration, func):
        self.duration = duration
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'Wait for {self.duration} seconds...')
        time.sleep(self.duration)
        return self.func(*args, **kwargs)

    def eager_call(self, *args, **kwargs):
        print('Call without delay')
        return self.func(*args, **kwargs)

def delay(duration):
    """
    装饰器:推迟某个函数的执行。
    同时提供 .eager_call 方法立即执行
    """
    # 此处为了避免定义额外函数,
    # 直接使用 functools.partial 帮助构造 DelayFunc 实例
    return functools.partial(DelayFunc, duration)

Nuestra función comercial es muy simple, solo agregue

@delay(duration=2)
def add(a, b):
    return a+b

Eche un vistazo al proceso de ejecución

>>> add    # 可见 add 变成了 Delay 的实例
<__main__.DelayFunc object at 0x107bd0be0>
>>> 
>>> add(3,5)  # 直接调用实例,进入 __call__
Wait for 2 seconds...
8
>>> 
>>> add.func # 实现实例方法
<function add at 0x107bef1e0>

El sexto tipo: decoradores que pueden decorar

Al escribir en modo singleton en Python, hay tres formas comunes de escribir. Uno de ellos se implementa con decoradores.

La siguiente es la versión singleton de la versión decoradora que escribí yo mismo.

instances = {}

def singleton(cls):
    def get_instance(*args, **kw):
        cls_name = cls.__name__
        print('===== 1 ====')
        if not cls_name in instances:
            print('===== 2 ====')
            instance = cls(*args, **kw)
            instances[cls_name] = instance
        return instances[cls_name]
    return get_instance

@singleton
class User:
    _instance = None

    def __init__(self, name):
        print('===== 3 ====')
        self.name = name

Puedes ver que usamos la función de decoración singleton para decorar la clase de Usuario. Los decoradores no son muy comunes en las clases, pero mientras esté familiarizado con el proceso de implementación de los decoradores, no es difícil decorar las clases. En el ejemplo anterior, el decorador solo controla la generación de instancias de clase.

El proceso de su instanciación, puede consultar mi proceso de depuración aquí para comprenderlo.

 

Supongo que te gusta

Origin blog.csdn.net/pythonxuexi123/article/details/112886953
Recomendado
Clasificación