[英]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)
盡管所有的嵌套,上面的代碼看起來都很難理解。 是否有另一種方法來執行上述操作,或者這是唯一可用於此類事情的方法?
您可以編寫具有__call__
方法的類,而不是編寫一堆嵌套的def
。
聽起來你想要一個裝飾器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
一種解決方案如下所示:
讓我們從創建以下 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)
現在我們可以編寫如下內容:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
注意:
dec
不接受多個 arguments。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
以下兩段代碼是等價的:
@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")
它可能看起來有很多代碼,但您不必每次都編寫DelayedDecorator
和PolyArgDecoratorMeta
類。 您必須親自編寫的唯一代碼如下,相當短:
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
您可以避免記住“我需要打電話給這個嗎?” 通過從wait
function 中刪除func
參數,並記住始終調用您的裝飾器返回器。
它看起來像這樣:
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
總是返回裝飾器,所以你必須在裝飾你的函數時使用()
。
我認為它好一點:
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.