[英]Optional args, kwargs to decorator
I am using the following approach to pass in an optional argument to a 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)
The above code looks pretty difficult to follow though with all the nesting.尽管所有的嵌套,上面的代码看起来都很难理解。 Is there another approach to do the above, or is this the only method available for something like this?
是否有另一种方法来执行上述操作,或者这是唯一可用于此类事情的方法?
You can write classes having a __call__
method, instead of writing a bunch of nested def
s.您可以编写具有
__call__
方法的类,而不是编写一堆嵌套的def
。
It sounds like you want a decorator Wait
which haults program execution for a few seconds.听起来你想要一个装饰器
Wait
来拖拽程序执行几秒钟。 If you don't pass in a Wait-time then the default value is 1 seconds.如果您不传递等待时间,则默认值为 1 秒。 Use-cases are shown below.
用例如下所示。
##################################################
@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)
When Wait
has an argument, such as @Wait(3)
, then the call Wait(3)
is executed before anything else happens.当
Wait
有一个参数时,例如@Wait(3)
,则调用Wait(3)
会在其他任何事情发生之前执行。
That is, the following two pieces of code are equivalent也就是下面两段代码是等价的
@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)
This is a problem.这是个问题。
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
One solution is shown below:一种解决方案如下所示:
Let us begin by creating the following class, DelayedDecorator
:让我们从创建以下 class,
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)
Now we can write things like:现在我们可以编写如下内容:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
Note that:注意:
dec
does not not accept multiple arguments. dec
不接受多个 arguments。dec
only accepts the function to be wrapped. dec
仅接受要包装的 function。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
The following two pieces of code are equivalent:以下两段代码是等价的:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
We can print "something"
to the console very slowly, as follows:我们可以非常缓慢地将
"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")
It may look like a lot of code, but you don't have to write the classes DelayedDecorator
and PolyArgDecoratorMeta
every-time.它可能看起来有很多代码,但您不必每次都编写
DelayedDecorator
和PolyArgDecoratorMeta
类。 The only code you have to personally write something like as follows, which is fairly short:您必须亲自编写的唯一代码如下,相当短:
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
You can avoid having to remember "do I need to call this or not?"您可以避免记住“我需要打电话给这个吗?” by removing the
func
argument from the wait
function, and remembering to always call your decorator-returner.通过从
wait
function 中删除func
参数,并记住始终调用您的装饰器返回器。
It would look like this:它看起来像这样:
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'
You just have to remember that wait
will always return the decorator, so you have to use ()
when decorating your functions.你只需要记住
wait
总是返回装饰器,所以你必须在装饰你的函数时使用()
。
I think it is a little bit better:我认为它好一点:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.