次の2つのコードはほぼ同等であることはよく知られています。
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
よくある間違いは@
、左端の議論を単に隠すだけだと考えることです。
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
上記がどのように@
機能するかでデコレータを書く方がはるかに簡単です。残念ながら、それは物事が行われている方法ではありません。
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)
は他に何が起こる前に実行されます。
つまり、次の2つのコードは同等です。
@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
1つのソリューションを以下に示します。
まず、次のクラスを作成します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)
これで次のように書くことができます:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
ご了承ください:
dec
複数の引数を受け入れません。
dec
ラップされる関数のみを受け入れます。
インポート検査クラス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)最後に:pass return r
インポート時間クラス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
次の2つのコードは同等です。
@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
execute_complete_reservation
2つのパラメーターを受け取りますが、1つ渡します。デコレータは、他の関数内で関数をラップするための単なる構文上の砂糖です。詳細なドキュメントについては、docs.python.org / reference / compound_stmts.html#functionをご覧ください。