LoginSignup
5
4

More than 5 years have passed since last update.

オプション引数ありデコレータの括弧を省略可能にする

Posted at

wraptのドキュメントを読んでいて、

  • オプション引数でデコレータをパラメータ化できる
  • オプション引数を指定しないときは括弧を省略できる
  • ただしオプション引数はキーワード引数として指定する必要あり

という興味深い性質を持つデコレータの書き方を知ったので紹介します。

wrapt を使う場合の例

def with_optional_arguments(wrapped=None, myarg1=1, myarg2=2):
    if wrapped is None:
        return functools.partial(with_optional_arguments,
                                 myarg1=myarg1, myarg2=myarg2)

    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        return wrapped(*args, **kwargs)

    return wrapper(wrapped)

ですが、functools.wraps でも以下のように書けば同じことが実現できます。

def with_optional_arguments(wrapped=None, myarg1=1, myarg2=2):
    if wrapped is None:
        return functools.partial(with_optional_arguments,
                                 myarg1=myarg1, myarg2=myarg2)

    @functools.wraps(wrapped)
    def wrapper(*args, **kwargs):
        return wrapped(*args, **kwargs)

    return wrapper

オプション引数を指定する場合

@with_optional_arguments(myarg1=3, myarg2=4)
def function():
    pass

のように書けて

  • まず、with_optional_arguments(myarg1=3, myarg2=4) が評価される
    • 引数 wrapped が指定されていないので partial で myarg1, myarg2 を束縛
  • 続けて、その結果で function をデコレートする
    • function = with_optional_arguments(myarg1=1, myarg2=2)(function)

という流れでデコレータが適用されます。

一方オプション引数を指定しない場合は

@with_optional_arguments()
def function():
    pass

と書くと

  • まず、with_optional_arguments() が評価される
    • 引数 wrapped もmyarg1, myarg2 も指定されていないので partial で myarg1=1, myarg2=2 を束縛
  • 続けて、その結果で function をデコレートする
    • function = with_optional_arguments()(function)

という処理になり、括弧を書かなかった場合

@with_optional_arguments
def function():
    pass

は単に

  • function をデコレート
    • function = with_optional_argument(function)
    • デフォルト値の myarg1=1, myarg2=2 が使われる

という処理になり、結果は空の括弧をつけた場合と括弧をつけない場合で同等になります。

このデコレータの定義方法を利用すると例えば

  • 最初はデコレータのパラメータ要らないだろうと考えて、使う側では括弧なしで書いていた。
  • 後から拡張によりパラメータを足したくなったが、既存のオプション引数指定しない箇所に空括弧つけて回るのは面倒だし無駄な感じがする

という状況をいい感じに乗り越えることができます。

なお、利用時の注意点として、キーワード引数とするところを誤って位置引数で指定してしまうと、本来関数が渡るべき第一引数に位置引数が入るため、わかりにくいエラーになったり誤動作したりするので気をつけましょう。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4