Pythonで自作のデコレータ(decorator)記述方法についてメモ。
functoolsパッケージを利用すると、デコレータ式適用後にも元関数の__name__
や__doc__
を簡単に引き継げる。(汎用デコレータでなければ不要かもしれない)
引数無しデコレータ
@my_deco def func(a, b, c): """target function""" print("a={} b={} c={}".format(a, b, c)) # func = my_deco(func)と等価 func(1, 2, c=3) print("{}:{}".format(func.__name__, func.__doc__))
import functools # クラスによる実装 class my_deco: def __init__(self, f): self._f = f functools.update_wrapper(self, f) def __call__(self, *args, **kwargs): print("pre-call: {}".format(self._f.__name__)) self._f(*args, **kwargs) # 関数による実装 def my_deco(f): @functools.wraps(f) def deco_wrap(*args, **kwargs): print("pre-call: {}".format(f.__name__)) f(*args, **kwargs) return deco_wrap
引数付きデコレータ
@my_deco('x', 'y') def func(a, b, c): """target function""" print("a={} b={} c={}".format(a, b, c)) # func = my_deco('x', 'y')(func) と等価 func(1, 2, c=3) print("{}:{}".format(func.__name__, func.__doc__))
import functools # クラスによる実装 class my_deco: def __init__(self, x, y): self._x = x self._y = y def __call__(self, f): @functools.wraps(f) def deco_wrap(*args, **kwargs): print("pre-call:x={} y={}".format(self._x, self._y)) f(*args, **kwargs) return deco_wrap # 関数による実装 def my_deco(x, y): def wrap(f): @functools.wraps(f) def deco_warp(*args, **kwargs): print("pre-call:x={},y={}".format(x, y)) f(*args, **kwargs) return deco_warp return wrap
おまけ:Lisp風の何か
from __future__ import print_function # for Python 2.x import functools my_deco = (lambda x, y: (lambda f: functools.wraps(f) (lambda *args, **kwargs: (print("pre-call:x={},y={}".format(x, y)), f(*args, **kwargs) ))))
関連URL