Python で引数を取るデコレータの作り方

デコレータ自身がパラメータを受け取りたい場合、関数をもう一段ネストさせます。「デコレータを返す関数」を作るイメージです。

基本構造

引数を取るデコレータは 3 層構造になります。

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

使い方は以下のとおりです。

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()

実行すると「Hello!」が 3 回出力されます。@repeat(3)repeat(3) を呼び出してデコレータを取得し、それを関数に適用しています。つまり say_hello = repeat(3)(say_hello) と同じ意味です。

実用例:リトライ処理

失敗時に再試行するデコレータを作ってみましょう。

import time

def retry(max_attempts, delay=1):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt < max_attempts - 1:
                        print(f"リトライ {attempt + 1}/{max_attempts}: {e}")
                        time.sleep(delay)
                    else:
                        raise
        return wrapper
    return decorator

@retry(max_attempts=3, delay=2)
def fetch_data():
    # ネットワーク処理など
    pass

このように引数付きデコレータを使うと、デコレータの挙動を柔軟にカスタマイズできます。