一般的に、引数のないデコレータは括弧なしで使用し、引数のあるデコレータは括弧付きで使用します。例えば:
- 引数なしデコレータ
def log(func): # デコレータは関数を引数として受け取り、関数を返す
@functools.wraps(func)
def wrapper(*args, **kwargs): # 元の関数と同じ機能をサポート
print(f'関数呼び出し: {func.__name__} 引数: {args} {kwargs}')
return func(*args, **kwargs) # 内部で元の関数を呼び出す
return wrapper # 元の関数と同じ機能を持つ関数を返す
@log # 使用時は括弧不要
def add(a, b):
return a + b
- 引数ありデコレータ(デコレータを返す関数)
def log(show_result=True):
def _log(func): # デコレータは関数を引数として受け取り、関数を返す
@functools.wraps(func)
def wrapper(*args, **kwargs): # 元の関数と同じ機能をサポート
print(f'関数呼び出し: {func.__name__} 引数: {args} {kwargs}')
start_time = time.time()
result = func(*args, **kwargs) # 内部で元の関数を呼び出す
if show_result:
print(f'結果: {result} 処理時間: {time.time()-start_time}')
return result
return wrapper # 置き換えられた新しい関数を返す
return _log
@log() # デフォルトパラメータを使用する場合でも括弧が必要(デコレータを取得するため)
def add(a, b):
return a + b
注意: 引数付きデコレータはパラメータによるカスタマイズをサポートしますが、使用時は括弧が必要です。ユーザーは括弧を忘れたり、必要があるかどうかを判断しにくくなることがあります。
pytest.fixtureのように、引数なしでも引数付きでも使用できるデコレータはどうでしょうか。例:
import pytest
@pytest.fixture
def a(): ...
@pytest.fixture()
def b(): ...
@pytest.fixture(scope='module')
def c(): ...
これを実現するには、デコレータの外側の関数がデコレータ自体(元の関数を受け取り、同機能の関数を返す)として機能し、パラメータ呼び出し後にデコレータを返す必要があります。実装は以下の通りです:
def logger(func=None, show_output=False): # 最初のパラメータは関数
def _logger(func): # デコレータは関数を引数として受け取り、関数を返す
@functools.wraps(func)
def wrapper(*args, **kwargs): # 元の関数と同じ機能をサポート
print(f'関数実行: {func.__name__} 引数: {args} {kwargs}')
start_time = time.time()
result = func(*args, **kwargs) # 内部で元の関数を呼び出す
if show_output:
print(f'実行結果: {result} 処理時間: {time.time()-start_time}')
return result
return wrapper # 置き換えられた新しい関数を返す
if func is not None: # 括弧なし(デコレータとして)使用時
return _logger(func) # 同機能の関数wrapperを返す
return _logger # そうでない場合、括弧付き呼び出し(デコレータを返す関数として)使用時、デコレータ_loggerを返す
@logger # デフォルトパラメータを使用する場合でも括弧不要
def add(a, b):
return a + b
@logger()
def sub(a, b):
return a - b
@logger(show_output=True)
def mul(a, b):
return a * b
注意:1. デコレータlogger()を使用して最初のパラメータfuncを手動で指定することはできません 2. パラメータを指定する際は、key=value形式のみ使用可能です