Optional args, kwargs to decorator



我使用以下方法将可选参数传递给装饰器:

def wait(func=None, delay=1.0):
def decorator_wait(func):
def wrapper_wait(*args, **kwargs):
time.sleep(delay)
return func(*args, **kwargs)
return wrapper_wait
return decorator_wait(func) if func is not None else decorator_wait

@wait
def print_something(something):
print (something)
@wait(delay=0.2)
def print_something_else(something):
print (something)

尽管有了所有的嵌套,但上面的代码看起来很难理解。有没有其他方法可以做到这一点,或者这是唯一可以用来做类似事情的方法?

通过从wait函数中删除func参数,并记住始终调用decorator returner,可以避免记住"我是否需要调用这个?">

它看起来是这样的:

def wait(delay=1.0):
def decorator_wait(func):
def wrapper_wait(*args, **kwargs):
time.sleep(delay)
return func(*args, **kwargs)
return wrapper_wait
return decorator_wait

@wait()
def print_something(something):
print (something)
@wait(delay=0.2)
def print_something_else(something):
print (something)
print_something("hello")
# 1 second delay, then 'hello'
print_something_else("there")
# 0.2 second delay, then 'there'

您只需要记住,wait将始终返回decorator,因此在装饰函数时必须使用()

我认为它稍微好一点:

import functools
import time
def wait(func=None, delay=1.0):
if func is None:
return lambda func: wait(func=func, delay=delay)
@functools.wraps(func) # this is good practice to use it see: https://stackoverflow.com/questions/308999/what-does-functools-wraps-do
def _wrapper(*args, **kwargs):
time.sleep(delay)
return func(*args, **kwargs)
return _wrapper

@wait
def test():
return
@wait(delay=3)
def test2():
return

您可以编写具有__call__方法的类,而不是编写一堆嵌套的defs。

听起来你想要一个装饰器Wait程序执行几秒钟。如果你没有通过等待时间则默认值为1秒。用例如下所示。

##################################################
@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)

Wait有一个参数,例如@Wait(3)时,调用Wait(3)在发生任何其他事情之前执行

也就是说,以下两段代码是等效的

@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)

这是个问题。

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

一种解决方案如下所示:

让我们从创建以下类DelayedDecorator:开始

import io
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)

现在我们可以写这样的东西:

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

注意:

  • dec不接受多个参数
  • dec只接受要封装的函数
import inspect
class PolyArgDecoratorMeta(type):
def __call__(Wait, *args, **kwargs):
try:
arg_count = len(args)
if (arg_count == 1):
if callable(args[0]):
SuperClass = inspect.getmro(PolyArgDecoratorMeta)[1]
r = SuperClass.__call__(Wait, args[0])
else:
r = DelayedDecorator(Wait, *args, **kwargs)
else:
r = DelayedDecorator(Wait, *args, **kwargs)
finally:
pass
return r

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 

以下两段代码是等效的:

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

我们可以非常缓慢地将"something"打印到控制台,如下所示:

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") 

最终注释

它看起来可能有很多代码,但您不必每次都编写类DelayedDecoratorPolyArgDecoratorMeta。你必须亲自编写的唯一代码如下,相当短:

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

最新更新