Pythonにおけるクロージャとデコレータの活用ガイド

クロージャ(Closure)の仕組み

Pythonにおけるクロージャとは、外側のスコープで定義された変数を、内側の関数が保持し続ける仕組みを指します。外側の関数が実行を終えた後でも、内側の関数はその変数を参照し続けることができるため、状態を保持する関数を作成する際に非常に有効です。

クロージャを構成する3つの条件

  • 関数の中にさらに関数が定義されている(入れ子構造)。
  • 内側の関数が、外側の関数のスコープにある変数を利用している。
  • 外側の関数が、内側の関数オブジェクトを戻り値として返している。
def power_factory(exponent):
    # 外側のスコープの変数: exponent
    def calculate_power(base):
        # 内側の関数が外側の変数を参照
        return base ** exponent
    
    return calculate_power

# 二乗を計算するクロージャの生成
square = power_factory(2)
print(square(5))  # 出力: 25

# 三乗を計算するクロージャの生成
cube = power_factory(3)
print(cube(5))    # 出力: 125

nonlocalキーワードによる状態の更新

クロージャ内部で外側の変数を更新したい場合は、nonlocalキーワードを使用します。これにより、グローバル変数を使わずにデータのカプセル化が可能になります。

def create_accumulator(initial_value=0):
    current_sum = initial_value

    def add(value):
        nonlocal current_sum
        current_sum += value
        return current_sum
    
    return add

acc = create_accumulator(10)
print(acc(5))  # 15
print(acc(20)) # 35

デコレータ(Decorator)の基本と応用

デコレータは、既存の関数やメソッドの定義を変更することなく、機能を追加したり拡張したりするための仕組みです。実体は、関数を引数として受け取り、新しい関数を返す「高階関数」です。ログ出力、認証チェック、実行時間の計測などに頻繁に利用されます。

基本的なデコレータの構造

デコレータを適用するには、@デコレータ名というシンタックスシュガーを使用します。

def simple_logger(func):
    def wrapper(*args, **kwargs):
        print(f"--- {func.__name__} の実行開始 ---")
        result = func(*args, **kwargs)
        print(f"--- {func.__name__} の実行終了 ---")
        return result
    return wrapper

@simple_logger
def process_data(data):
    print(f"データを処理中: {data}")

process_data("Sample text")

引数を持つデコレータ(デコレータファクトリ)

デコレータ自体に引数を渡したい場合は、さらにもう一層関数を重ねる「デコレータファクトリ」を作成します。

def access_control(role):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"アクセス権限を確認中: {role}")
            if role != "admin":
                return "アクセス拒否: 権限が不足しています"
            return func(*args, **kwargs)
        return wrapper
    return decorator

@access_control(role="user")
def delete_database():
    return "データベースを削除しました"

print(delete_database()) # アクセス拒否メッセージが表示される

複数のデコレータを重ねる

一つの関数に複数のデコレータを適用することも可能です。この場合、上から順番に適用されます。

def bold(func):
    def wrapper():
        return f"<b>{func()}</b>"
    return wrapper

def italic(func):
    def wrapper():
        return f"<i>{func()}</i>"
    return wrapper

@bold
@italic
def get_text():
    return "Hello Python"

print(get_text())  # <b><i>Hello Python</i></b>

クラスデコレータ

デコレータは関数だけでなく、クラスに対しても適用できます。クラスのメタデータを変更したり、新しいメソッドを動的に追加したりする際に利用します。

def add_version(cls):
    cls.version = "1.0.0"
    def get_version(self):
        return self.version
    cls.get_version = get_version
    return cls

@add_version
class Application:
    def __init__(self, name):
        self.name = name

app = Application("Task Manager")
print(app.version)       # 1.0.0
print(app.get_version()) # 1.0.0

タグ: Python closure decorator MetaProgramming

6月21日 00:36 投稿