yohhoyの日記

技術的メモをしていきたい日記

自作デコレータの書き方

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