Yomiage
メニュー

Python実践講座 15:デコレータとコンテキスト管理

更新 2026/9/27 6:22:11 公開

投稿者: しゅりりん

到達点は、共通処理を関数へ付け加え、資源の利用範囲を明示することです。前提は第06・09回です。

関数も値として渡せる

デコレータは関数を受け取り、別の関数などを返して振る舞いを包みます。ログや計測に便利ですが、何が起きるか読めないほど重ねると保守が難しくなります。まず手動で包む処理を理解します。

from contextlib import contextmanager
from functools import wraps


def traced(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        print(f"開始: {function.__name__}")
        try:
            return function(*args, **kwargs)
        finally:
            print(f"終了: {function.__name__}")
    return wrapper


@contextmanager
def section(name):
    print(f"入る: {name}")
    try:
        yield
    finally:
        print(f"出る: {name}")


@traced
def total(values):
    return sum(values)


with section("集計"):
    print(total([10, 20]))
print(total.__name__)

main.pyでは、集計へ入る、total開始、total終了、30、集計を出る、totalという順で表示されます。wrapsは元の関数名や説明などを保つ助けになります。*args と **kwargs は位置引数とキーワード引数を受け渡します。

後始末は成功時だけではない

contextmanagerのyield前が開始処理、後ろが終了処理です。例外でも終了処理を走らせるため、try/finallyで囲みます。中の例外を捕まえて再送出しなければ、失敗を抑止する動きになるので意図を明確にします。

今回のデコレータは同期関数向けです。async関数にそのまま付けると、コルーチンを作った時点しか囲まず、実際の非同期処理全体を計測できません。非同期版ではラッパー自体をasyncにしてawaitする必要があります。

便利な抽象化を使う前に、呼び出し順、戻り値、例外、資源の寿命が変わらないか確認します。認証やトランザクションなど重要な処理を隠す場合は、特にテストで契約を確かめます。

練習と解答

練習:totalの中でValueErrorを投げると、どの終了メッセージが出るでしょうか。

解答:totalの終了と集計の終了の両方が表示され、例外は外側へ伝わります。finallyがあるからです。例外を無視して30を返すような動作にはなりません。

公式資料

contextlibとfunctoolsが参照先です。


Python全20回の目次 | 前の回 | 次の回

このブラウザでは Web Speech API が利用できません。

1 / 16 · 停止中