デコレーターしたい(@decorator

1@デコレーター名
2def 関数名(引数):
3    ...

@デコレーター名で、関数やクラス本体の処理は変更せずに、補足処理を追加できます。

デコレーターを書きたい

 1from functools import wraps
 2
 3def decorator_name(f):
 4    @wraps(f)
 5    def wrapper(*args, **kwargs)
 6        ...前処理
 7        result = f(*args, **kwargs)  # 元の関数を実行
 8        ...処理
 9        return result  # 元の関数の実行結果
10    return wrapper

デコレータを自作するときのテンプレートのようなものを用意しました。

デコレーターの正体は 高階関数 で、引数が関数オブジェクトになっていて、返り値も内部処理した関数オブジェクトになっています。

まず、デコレーター関数(decorator_name)の引数を任意の関数(f)とします。 そして、デコレーター関数の中に、 ラッパー関数(wrapper)を作成します。 ラッパー関数の引数は可変長配列((*args, **kwargs))にし、 元の関数の引数を受け取れるようにします。 また、ラッパー関数の返り値は、元の関数の実行値にします。 最後に、ラッパー関数オブジェクトを、デコレーター関数の返り値とします。

このデコレーター関数(decorator_name)の返り値を ラッパー関数(wrapper)オブジェクトにするのがポイントです。

ヒント

ラッパー関数には@functools.wrapsデコレーターを使っています。 このデコレーターを使うことで、 元の関数のメタデータ情報(__name____doc__など)を 引き継ぐ(残す?)ことができます。

以下では、作っておくとちょっと便利になるデコレーターを考えてみました。 動作確認していないものもあります。動かなかった場合は、教えてください。

ストップウォッチしたい

 1import time
 2from functools import wraps
 3
 4def timer(f):
 5    @wraps(f)
 6    def wrapper(*args, **kwargs):
 7        start_time = time.perf_counter()
 8        result = f(*args, **kwargs)
 9        end_time = time.perf_counter()
10        elapsed_time = end_time - start_time
11        print(f"{f.__name__} executed in {elapsed_time:.4f} seconds")
12        return result
13    return wrapper
14
15@timer
16def some_function(...):
17    ...
18    return ...

関数の実行時間を計測したい場合のデコレーターです。 @timerデコレーターで、任意の関数の実行時間を標準出力で確認できます。

time.perf_counterを使うことで、より高精度な実行時間を取得できます。 また、CPUの実行時間だけを取得したい場合はtime.process_timeに置き換えればOKです。

リトライしたい

 1import time
 2from functools import wraps
 3
 4def retry(max_retries=3, delay=1):
 5    def decorator(f):
 6        @wraps(f)
 7        def wrapper(*args, **kwargs):
 8            retries = 0
 9            while retries < max_retries:
10                try:
11                    return f(*args, **kwargs)
12                except Exception as e:
13                    retries += 1
14                    time.sleep(delay)
15            raise Exception(f"Function {f.__name__} failed after {max_retries} retries.")
16        return wrapper
17    return decorator
18
19@retry
20def some_function(引数):
21    ...
22    return

例外が発生する可能性がある処理に対して、リトライ機能を追加するデコレーターです。

リファレンス