Python講座 / デコレーターとコンテキストマネージャー
デコレーターは関数の呼び出しを包み、コンテキストマネージャーはブロックの前後の準備と片付けを管理します。共通処理をまとめられますが、元の戻り値と例外の契約を意図して保つ必要があります。
呼び出しの拡張と寿命の管理を分ける
@decoratorは定義した関数をデコレーターへ渡し、その結果を関数名へ結び付ける実際の処理です。コメントではありません。withは開始と終了の処理を呼び、本文で例外が起きた場合の片付けも組み立てられます。
functools.wrapsは元の関数名や説明などのメタデータを保つために使います。それだけで型、安全性、例外処理などのすべての保証を保ったことにはなりません。
ラッパーと一時リストを実行する
wrappers.pyへ全体を保存し、python wrappers.pyを実行します。
from functools import wraps
from contextlib import contextmanager
def announce(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("start")
return func(*args, **kwargs)
return wrapper
@contextmanager
def temporary_items():
items = []
try:
yield items
finally:
items.clear()
print("cleared")
@announce
def total(amounts):
return sum(amounts)
with temporary_items() as items:
items.extend([3500, 8000])
print(total(items))
print(total.__name__)
print(items)
予想結果です。関数名はtotalのまま、withを抜けたリストは空になります。
start 11500 cleared total []
値と例外が通る経路を追う
contextmanagerを使うジェネレーターは一回yieldして、with本文へ値を渡します。終了時はその後の処理へ戻り、finallyに置いた片付けは本文の失敗時にも動きます。例外を捕まえて消すと失敗を隠すため、意図した抑制でない限り伝えます。
ラッパーでreturnを忘れると、元の関数の結果ではなくNoneが返ります。メッセージが出たかだけでなく、引数の受け渡し、戻り値、例外の伝播を確認します。
後処理を試して抽象化を選ぶ
with本文で意図的にValueErrorを起こし、clearedが出てリストが片付けられることを確認します。別の練習でラッパーのreturnを一時的に外し、結果の変化を見てから元へ戻します。
再試行のデコレーターは状態を変えるリクエストを繰り返し、取引を重複させる場合があります。再試行の方針と資源の後処理を混同しないでください。一回だけの小さな操作なら、新しい仕組みを作らず書く方が明確な場合もあります。基になる流れは例外とfinallyで確認できます。
@announce は定義後の func = announce(func) に相当します。wrapsは名前や説明などのメタ情報を保ち、引数検証を代行しません。contextmanagerは開始処理→yieldでwith本文へ→正常終了または例外→finallyの片付けという順です。yield付近で例外を捕まえて再送出しないと抑制される場合があるため、意図した回復か確認します。
動作と適用条件の公式説明:Python: functools.wraps




