in

python – ¿Decoradores con parámetros?

apple touch icon@2

Es bien sabido que las siguientes dos piezas de código son casi equivalentes:

@dec
def foo():
    pass    foo = dec(foo)

############################################
foo = dec(foo)

Un error común es pensar que @ simplemente oculta el argumento más a la izquierda.

@dec(1, 2, 3)
def foo():
    pass    
###########################################
foo = dec(foo, 1, 2, 3)

Sería mucho más fácil escribir decoradores si lo anterior es cómo @ trabajó. Desafortunadamente, esa no es la forma en que se hacen las cosas.


Considere un decorador Waitque interrumpe la ejecución del programa durante unos segundos. Si no pasa un tiempo de espera, el valor predeterminado es 1 segundo. Los casos de uso se muestran a continuación.

##################################################
@Wait
def print_something(something):
    print(something)

##################################################
@Wait(3)
def print_something_else(something_else):
    print(something_else)

##################################################
@Wait(delay=3)
def print_something_else(something_else):
    print(something_else)

Cuando Wait tiene un argumento, como @Wait(3), luego la llamada Wait(3)
es ejecutado antes de pasa cualquier otra cosa.

Es decir, los siguientes dos fragmentos de código son equivalentes

@Wait(3)
def print_something_else(something_else):
    print(something_else)

###############################################
return_value = Wait(3)
@return_value
def print_something_else(something_else):
    print(something_else)

Esto es un problema.

if `Wait` has no arguments:
    `Wait` is the decorator.
else: # `Wait` receives arguments
    `Wait` is not the decorator itself.
    Instead, `Wait` ***returns*** the decorator

A continuación se muestra una solución:

Comencemos por crear la siguiente clase, DelayedDecorator:

class DelayedDecorator:
    def __init__(i, cls, *args, **kwargs):
        print("Delayed Decorator __init__", cls, args, kwargs)
        i._cls = cls
        i._args = args
        i._kwargs = kwargs
    def __call__(i, func):
        print("Delayed Decorator __call__", func)
        if not (callable(func)):
            import io
            with io.StringIO() as ss:
                print(
                    "If only one input, input must be callable",
                    "Instead, received:",
                    repr(func),
                    sep="n",
                    file=ss
                )
                msg = ss.getvalue()
            raise TypeError(msg)
        return i._cls(func, *i._args, **i._kwargs)

Ahora podemos escribir cosas como:

 dec = DelayedDecorator(Wait, delay=4)
 @dec
 def delayed_print(something):
    print(something)

Tenga en cuenta que:

  • dec no acepta múltiples argumentos.
  • dec solo acepta la función que se va a envolver.

    importar inspeccionar clase PolyArgDecoratorMeta (tipo): def llama(Espere, * args, ** kwargs): intente: arg_count = len (args) if (arg_count == 1): if in invocable (args[0]): SuperClass = inspeccionar.getmro (PolyArgDecoratorMeta)[1]
    r = Superclase.llama(Espera, argumentos[0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) finalmente: pass return r

    clase de tiempo de importación Wait (metaclass = PolyArgDecoratorMeta): def en eso(i, func, delay = 2): i._func = func i._delay = delay

    def __call__(i, *args, **kwargs):
        time.sleep(i._delay)
        r = i._func(*args, **kwargs)
        return r 
    

Los siguientes dos fragmentos de código son equivalentes:

@Wait
def print_something(something):
     print (something)

##################################################

def print_something(something):
    print(something)
print_something = Wait(print_something)

Podemos imprimir "something" a la consola muy lentamente, de la siguiente manera:

print_something("something")

#################################################
@Wait(delay=1)
def print_something_else(something_else):
    print(something_else)

##################################################
def print_something_else(something_else):
    print(something_else)

dd = DelayedDecorator(Wait, delay=1)
print_something_else = dd(print_something_else)

##################################################

print_something_else("something")

Notas finales

Puede parecer mucho código, pero no es necesario que escriba las clases. DelayedDecorator y PolyArgDecoratorMeta cada vez. El único código que tiene para escribir personalmente algo como lo siguiente, que es bastante corto:

from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Wait(metaclass=PolyArgDecoratorMeta):
 def __init__(i, func, delay = 2):
     i._func = func
     i._delay = delay

 def __call__(i, *args, **kwargs):
     time.sleep(i._delay)
     r = i._func(*args, **kwargs)
     return r

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Programación de dardos: enumeración

EeiSmyD3QSGkPSHsp7DtHc 1200 80

Dragon’s Dogma 2 funcionará con el mismo motor que Resident Evil Village, dice un filtrador