Python のdecorator について エキスパートPython プログラミングのメモ
競プロ関係ではないんですが、勉強していてわからないところがあったんでメモしておきます。
エキスパートPythonプログラミングではデコレータ
def some_decorator(function): def wrapped(*args, **kargs): # 関数を呼び出す前に行う処理 result = function(*args, **kargs) # 呼び出し後に行う処理 return(result) # 引数にとったfunction をデコレートした関数を返す。 return(wrapped) @some_decorator def decorated_function(): pass
は以下のような関数再定義の構文糖衣であると説明されている。
def decorated_function(): pass decorated_function = some_decorator(decorated_function)
ここでパラメータ付きのデコレータ
def repeat(number=3): def actual_decorator(function): def wrapper(*args, **kargs): result = None for _ in range(number): result = function(*args, **kargs) return(result) return(wrapper) return(actural_decorator)
を考える。repeat
そのものはデコレータではなく、パラメータを受け取り、デコレータactual_decorator
を返す関数である。このときacutual_decorator
はrepeat
が受け取るパラメータによって変更されている。
実際のデコレータはrepeat
そのものではなくこれが返す関数なのであるから、デコレートされる関数を定義するときは、@repeat
ではなく@repeat(x)
としなければいけない。デフォルトのパラメータ値を用いるときであっても@repeat()
である。
@repeat() def foo(): print("foo")
>>> foo() foo foo foo
以下のように() をつけずに作用させるとエラーになる。
# 失敗例 @repeat def foo(): print("foo")
>>> foo() --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-260-624891b0d01a> in <module>() ----> 1 foo() TypeError: actual_decorator() missing 1 required positional argument: 'function'
後者の例は以下の関数再定義と同義であるので、foo 自身がデコレータ(関数を受け取って修飾を施す関数)になってしまっている。
foo = repeat(foo) # foo の返り値はacutual_decorator
>>> ?foo
Signature: foo(function)