Pythonでデコレータ関数を使う
この記事では、Pythonのデコレータ関数について説明します。デコレータがどのように動作するか、そして、いくつかの実践的な例を通して、コードをより簡潔で読みやすくするための方法を学びます。
目次
デコレータとは?
デコレータは、関数を修飾して、その関数の動作を変更または拡張する機能です。関数の前処理や後処理を行うために使用されます。既存の関数に新たな機能を追加したい場合に、コードの重複を避けて簡潔に記述できます。
def my_decorator(func):
def wrapper():
print("Before function execution")
func()
print("After function execution")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
上の例では、`my_decorator` が `say_hello` 関数を修飾しています。`@my_decorator` は、`say_hello = my_decorator(say_hello)` と同じ意味です。`say_hello()` を実行すると、デコレータによって追加された処理が実行されます。
デコレータの構文と基本的な使い方
デコレータは `@` 記号を使って関数の前に配置します。デコレータ関数は、修飾される関数を引数として受け取り、修飾後の関数を返します。
def make_bold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def make_italic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@make_bold
@make_italic
def get_message():
return "Hello"
この例では、`get_message` 関数は `make_bold` と `make_italic` の2つのデコレータで修飾されています。実行結果は"<b><i>Hello</i></b>"となります。
デコレータの引数付きデコレータ
デコレータは引数をとることもできます。この場合、デコレータ関数は引数を受け取る関数(デコレータファクトリ)を返し、その関数が修飾する関数を引数として受け取ります。
def repeat(num):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num=3)
def greet(name):
print(f"Hello, {name}!")
greet("World")
この例では、`repeat`デコレータは繰り返し回数を引数として受け取ります。`greet`関数は3回実行されます。
実践的な例:ロギングデコレータ
関数の呼び出しとその結果をログに出力するデコレータです。
import logging
logging.basicConfig(level=logging.INFO)
def log_execution(func):
def wrapper(*args, **kwargs):
logging.info(f"Calling function {func.__name__} with args {args} and kwargs {kwargs}")
result = func(*args, **kwargs)
logging.info(f"Function {func.__name__} returned {result}")
return result
return wrapper
@log_execution
def add(a, b):
return a + b
add(5, 3)
このデコレータは関数の呼び出しと戻り値をログに出力します。
実践的な例:計測デコレータ
関数の実行時間を計測するデコレータです。
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"Function {func.__name__} took {execution_time:.4f} seconds")
return result
return wrapper
@measure_time
def slow_function():
time.sleep(1)
return "Done"
slow_function()
このデコレータは関数の処理時間を計測して出力します。
デコレータの注意点
デコレータを使用する際の注意点です。
- デコレータが多重に適用される場合、デコレータの適用順序に注意する必要があります。
- デコレータは、関数の属性(`__name__`, `__doc__`など)を変更する可能性があります。`functools.wraps` を使用して、元の関数の属性を保持するようにしましょう。
- 複雑なデコレータは可読性を低下させる可能性があるため、必要に応じて適切なコメントを追加するなどの工夫が必要です。