上級プログラマーのための Python デコレータ ガイド

Python デコレータは、関数やメソッドの動作を変更するための強力で柔軟なツールです。プログラマーは、これらを使用して、呼び出し可能なオブジェクトの機能を、わかりやすく、読みやすく、保守しやすい方法で拡張または変更できます。この記事では、ネストされたデコレータ、デコレータ引数、クラスベースのデコレータなど、Python デコレータに関連する高度な概念について説明します。

デコレータとは何ですか?

デコレータは、別の関数の動作を変更する関数です。デコレータは別の関数をラップして、明示的にコードを変更せずにその動作を拡張します。デコレータは、@decorator_name 構文を使用して定義され、関数定義の上に配置されます。

基本的なデコレータ構文

シンプルなデコレータは、関数を引数として受け取り、何らかの動作を追加する内部関数を定義し、その内部関数を返します。

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

引数付きデコレータ関数

デコレータは、引数を受け入れることでより柔軟になります。このようなデコレータを作成するには、デコレータを返す関数を記述する必要があります。これにより、デコレータにさらに動的な動作を追加できます。

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

ネストデコレータ

デコレータはネストして複数の動作を組み合わせることができます。たとえば、1 つの関数に 2 つ以上のデコレータを使用できます。

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def repeat_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + result
    return wrapper

@repeat_decorator
@uppercase_decorator
def say_word(word):
    return word

print(say_word("hello"))

クラスベースのデコレータ

Python では、__call__ メソッドを使用して、デコレータをクラスとして実装することもできます。クラスベースのデコレータは、より複雑な状態管理と動作が必要な場合に便利です。

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()
say_hello()

functools.wraps を使用してメタデータを保存する

デコレータを記述すると、デコレータされた関数は、名前やドキュメント文字列などの元のメタデータを失います。 functools.wraps デコレータを使用すると、元の関数のメタデータをラッパー関数にコピーできます。

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Wrapper function executed before", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def display_info(name, age):
    """Displays name and age."""
    print(f"display_info ran with arguments ({name}, {age})")

print(display_info.__name__)  # Output: display_info
print(display_info.__doc__)   # Output: Displays name and age.

結論

Python デコレータは、柔軟なコード設計と動作の変更を可能にする強力な機能です。ネストされたデコレータ、引数付きのデコレータ、クラスベースのデコレータなどの高度な使用法により、Python プログラムにさらに多くの機能と読みやすさを提供できます。デコレータを正しく理解して使用することで、開発者はより簡潔で効率的で読みやすいコードを作成できます。