【Python】デコレータの概要とメリット


Pythonのデコレータは、関数やメソッドの動作を修正または拡張するための強力なツールです。デコレータを使うことで、コードの再利用性を高め、機能を簡潔に追加することができます。この記事では、デコレータの基本概念、メリット、およびいくつかのサンプルコードを紹介します。

1. デコレータの基本概念

デコレータは、他の関数を引数として受け取り、新しい関数を返す関数です。Pythonでは、デコレータは @ 記号を使って関数定義の前に付けます。以下の簡単な例を見てみましょう。

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()

上記のコードを実行すると、以下の出力が得られます:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

ここで、my_decoratorsay_hello 関数の前後にメッセージを追加する機能を持つ wrapper 関数を返しています。

2. デコレータのメリット

  1. コードの再利用性向上: デコレータを使用すると、共通の機能を一度だけ実装し、複数の関数で再利用することができます。
  2. コードの簡潔化: デコレータを使うことで、機能を関数の定義に組み込むことができ、コードがより簡潔で読みやすくなります。
  3. クロスカッティング関心事の分離: ロギング、認証、キャッシュなどの共通の機能をデコレータとして分離することで、ビジネスロジックとその他の関心事を分離できます。

3. よく使われるデコレータの例

3-1 ロギングデコレータ

関数の呼び出し前後にログを出力するデコレータの例です。

import functools

def log_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

add(3, 5)

実行結果

Calling add with args: (3, 5), kwargs: {}
add returned 8

3-2 認証デコレータ

ユーザーの認証をチェックするデコレータの例です。

def authenticate(user):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if not user.is_authenticated:
                raise PermissionError("User is not authenticated")
            return func(*args, **kwargs)
        return wrapper
    return decorator

class User:
    def __init__(self, authenticated):
        self.is_authenticated = authenticated

current_user = User(authenticated=True)

@authenticate(current_user)
def get_data():
    return "Sensitive data"

print(get_data())

実行結果

Sensitive data

funtoolsについて

functoolsは、Pythonの標準ライブラリの一部であり、関数プログラミング向けの便利なツールを提供するモジュールです。このモジュールには、関数の動作を変更または拡張するためのユーティリティ関数が含まれています。以下に、functoolsモジュールのいくつかの主要な機能を紹介します。

functools.wraps:

  • デコレータを作成するときに、元の関数の名前、モジュール、ドキュメンテーション文字列(docstring)などの属性を保持するために使用します。
  • デコレータでラップされた関数が元の関数として見えるようにするために便利です。
import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Calling decorated function")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """Example function docstring"""
    print("Example function")

print(example.__name__)  # 出力: example
print(example.__doc__)   # 出力: Example function docstring

functools.lru_cache:

  • 関数の結果をキャッシュして、再度同じ引数で呼び出されたときに計算を省略して結果を再利用するためのデコレータです。
  • 計算のコストが高い関数に対して非常に有用です。
import functools

@functools.lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  # 出力: 55

functools.partial:

  • 関数の一部の引数に固定値を設定し、新しい関数を作成するための関数です。
  • 複雑な関数を簡略化するために使用できます。
import functools

def multiply(x, y):
    return x * y

double = functools.partial(multiply, 2)
print(double(5))  # 出力: 10

まとめ

Pythonのデコレータは、コードの再利用性を高め、共通の機能を簡潔に追加するための強力なツールです。ロギング、認証、キャッシュなど、多くの場面でデコレータを活用することで、よりクリーンでメンテナブルなコードを書くことができます。是非、デコレータを使ってあなたのコードを改善してみてください。