PYTHON MEBY

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` を使用して、元の関数の属性を保持するようにしましょう。
  • 複雑なデコレータは可読性を低下させる可能性があるため、必要に応じて適切なコメントを追加するなどの工夫が必要です。

関連記事