¿Cuáles son algunos usos comunes de los decoradores de Python? [closed]

7 minutos de lectura

avatar de usuario
Dana

Si bien me gusta considerarme un codificador de Python razonablemente competente, un aspecto del lenguaje que nunca he podido asimilar son los decoradores.

Sé lo que son (superficialmente), he leído tutoriales, ejemplos, preguntas sobre Stack Overflow y entiendo la sintaxis, puedo escribir la mía, ocasionalmente uso @classmethod y @staticmethod, pero nunca se me ocurre usar un decorador para resolver un problema en mi propio código de Python. Nunca me encuentro con un problema en el que pienso, “Hmm… ¡este parece un trabajo para un decorador!”

Entonces, me pregunto si ustedes podrían ofrecer algunos ejemplos de dónde han usado decoradores en sus propios programas, y espero tener un “¡Ajá!” momento y obtener a ellos.

  • Además, los decoradores son útiles para memorizar, es decir, almacenar en caché el resultado de una función que tarda en calcularse. El decorador puede devolver una función que verifica las entradas y, si ya se han presentado, devolver un resultado almacenado en caché.

    – Pedro

    25/10/2014 a las 21:37

  • Tenga en cuenta que Python tiene un decorador incorporado, functools.lru_cacheque hace exactamente lo que dijo Peter, desde Python 3.2, lanzado en febrero de 2011.

    – Ignacio

    30 de julio de 2019 a las 4:30

  • Los Contenidos de la Biblioteca de decoradores de Python debería darle una buena idea de otros usos para ellos.

    – martineau

    27 de noviembre de 2019 a las 0:21


Utilizo decoradores principalmente para fines de sincronización.

def time_dec(func):

  def wrapper(*arg):
      t = time.clock()
      res = func(*arg)
      print func.func_name, time.clock()-t
      return res

  return wrapper


@time_dec
def myFunction(n):
    ...

  • Bajo Unix, time.clock() mide el tiempo de CPU. Es posible que desee utilizar time.time() en cambio, si desea medir el tiempo del reloj de pared.

    – Jabba

    4 de febrero de 2013 a las 17:45

  • ¡Gran ejemplo! Sin embargo, no tengo idea de lo que hace. Sería muy bueno una explicación de lo que está haciendo allí y cómo el decorador resuelve el problema.

    – MeLuz

    15/06/2014 a las 16:00

  • Bueno, mide el tiempo que tarda en myFunction correr …

    – RSabet

    14 de junio de 2015 a las 22:12


  • @time_dec es azúcar sintático para: myFunction = time_dec(myFunction). El resto es Python estándar.

    – DMeneses

    16 abr 2021 a las 23:34

avatar de usuario
Juan Fouhy

Los he usado para la sincronización.

import functools

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(f):
        @functools.wraps(f)
        def newFunction(*args, **kw):
            lock.acquire()
            try:
                return f(*args, **kw)
            finally:
                lock.release()
        return newFunction
    return wrap

Como se señaló en los comentarios, desde Python 2.5 puedes usar un with declaración junto con una threading.Lock (o multiprocessing.Lock desde la versión 2.6) para simplificar la implementación del decorador a solo:

import functools

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(f):
        @functools.wraps(f)
        def newFunction(*args, **kw):
            with lock:
                return f(*args, **kw)
        return newFunction
    return wrap

De todos modos, luego lo usas así:

import threading
lock = threading.Lock()

@synchronized(lock)
def do_something():
  # etc

@synchronzied(lock)
def do_something_else():
  # etc

Básicamente solo pone lock.acquire() / lock.release() a ambos lados de la llamada de función.

  • Posiblemente justificado, pero los decoradores son inherentemente confusos, especialmente. a los novatos de primer año que vienen detrás de ti e intentan modificar tu código. Evite esto con sencillez: simplemente haga que do_something() encierre su código en un bloque bajo ‘with lock:’ y todos podrán ver claramente su propósito. Los decoradores son utilizados en exceso por personas que quieren parecer inteligentes (y muchos lo son), pero luego el código llega a los simples mortales y se enfada.

    – Kevin J. Arroz

    25/11/2014 a las 16:40

  • @ KevinJ.Rice Restringir su código para que los ‘novatos de primer año’ puedan entenderlo mejor es una práctica terrible. La sintaxis de Decorator es mucho más fácil de leer y desacopla en gran medida el código.

    – Taylor Jones

    04/02/2015 a las 20:02

  • @TaylerJones, la legibilidad del código es casi mi máxima prioridad al escribir. El código se lee más de 7 veces por cada vez que se modifica. El código difícil de entender (para novatos o expertos que trabajan bajo presión de tiempo) es una deuda técnica que debe pagarse cada vez que alguien visita el árbol de código fuente.

    – Kevin J. Arroz

    5 de febrero de 2015 a las 20:28

  • @TaylerJones Una de las tareas más importantes para un programador es brindar claridad.

    – JDOaktown

    23 de junio de 2019 a las 20:15

  • @JDOaktown, una tarea importante para los programadores es poder comprender conceptos simples de los lenguajes con los que trabajan.

    – DMeneses

    16 abr 2021 a las 23:36

avatar de usuario
Simón

Utilizo decoradores para los parámetros de verificación de tipo que se pasan a mis métodos de Python a través de algún RMI. Entonces, en lugar de repetir el mismo conteo de parámetros, genera excepciones una y otra vez.

Por ejemplo, en lugar de:

def myMethod(ID, name):
    if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):
        raise BlaBlaException() ...

solo declaro:

@accepts(uint, utf8string)
def myMethod(ID, name):
    ...

y accepts() hace todo el trabajo por mí.

  • Para cualquier persona interesada, hay una implementación de @accepts en PEP 318.

    – martineau

    15 de septiembre de 2010 a las 11:23

  • Creo que hay un error tipográfico… el primer método debería aceptarse… declaraste ambos como “myMethod”

    – Desarrollador

    29 de abril de 2014 a las 13:05

  • @DevC No, no parece un error tipográfico. Dado que claramente no es una implementación de “acepta (…)”, y aquí “acepta (…)” hace el trabajo que de otro modo harían las dos líneas al comienzo de “myMethod (…)” — ese es el única interpretación que encaja.

    –Evgeni Sergeev

    24 de octubre de 2015 a las 12:23

  • Perdón por el golpe, solo quería señalar que verificar el tipo de argumentos pasados ​​y generar un TypeError de lo contrario se considera una mala práctica porque no aceptará, por ejemplo, un int si solo verifica los flotantes, y porque normalmente el el propio código debe adaptarse a diferentes tipos de valores pasados ​​para una máxima flexibilidad.

    – Gustavo6046

    16/04/2016 a las 21:16

  • La forma recomendada de realizar la verificación de tipos en Python es a través de la isinstance() función, como se hace en el PEP 318 implementación del decorador. Desde su classinfo El argumento puede ser de uno o más tipos, usarlo también mitigaría las objeciones (válidas) de @ Gustavo6046. Python también tiene un Number clase base abstracta, pruebas muy genéricas como isinstance(42, numbers.Number)es posible.

    – martineau

    27 de noviembre de 2019 a las 0:13


avatar de usuario
diario

Los decoradores se utilizan para cualquier cosa que desee “envolver” de forma transparente con funcionalidad adicional.

Django los usa para envolver Funcionalidad de “inicio de sesión requerido” en las funciones de vistaasí como para registro de funciones de filtro.

Puede usar decoradores de clase para agregar registros con nombre a las clases.

Cualquier funcionalidad suficientemente genérica que pueda “añadir” al comportamiento de una clase o función existente es un juego justo para la decoración.

También hay un discusión de casos de uso en el grupo de noticias Python-Dev señalado por PEP 318 — Decoradores de Funciones y Métodos.

Para las pruebas de nariz, puede escribir un decorador que suministre una función o método de prueba unitaria con varios conjuntos de parámetros:

@parameters(
   (2, 4, 6),
   (5, 6, 11),
)
def test_add(a, b, expected):
    assert a + b == expected

avatar de usuario
DNS

La biblioteca Twisted utiliza decoradores combinados con generadores para dar la ilusión de que una función asíncrona es síncrona. Por ejemplo:

@inlineCallbacks
def asyncf():
    doStuff()
    yield someAsynchronousCall()
    doStuff()
    yield someAsynchronousCall()
    doStuff()

Usando esto, el código que se habría dividido en un montón de pequeñas funciones de devolución de llamada se puede escribir de forma bastante natural como un solo bloque, lo que lo hace mucho más fácil de entender y mantener.

Un uso obvio es para iniciar sesión, por supuesto:

import functools

def log(logger, level="info"):
    def log_decorator(fn):
        @functools.wraps(fn)
        def wrapper(*a, **kwa):
            getattr(logger, level)(fn.__name__)
            return fn(*a, **kwa)
        return wrapper
    return log_decorator

# later that day ...
@log(logging.getLogger('main'), level="warning")
def potentially_dangerous_function(times):
    for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad