Drafts

@cm3 の草稿置場 / 少々Wikiっぽく使っているので中身は適宜追記修正されます。

引数を指定するタイミングをずらすのにはデコレータ的なものを冗長に書けばよい

追記:ここに書いてることって「カリー化」と「部分適用」を勉強したらもっと整理される気がするので、あまり参考にしないように。

def gen_func_1arg(func):
    def func_1arg(x):
        return func(x, 15)
    return func_1arg

@gen_func_1arg
def func_x(x, y):
    print(x + y)
    return 1

func_x(4)

…①

メインは xとy の合計をprintする func_x(x, y) なのだが、それをxだけの一変数関数オブジェクトとして後程使いまわしたい要求があって、yは実行時に argv 経由で与えられることを想定している。

すると、上のコードは y=15 が gen_func_1arg 関数内にハードコーディングされているので要求を満たしていない。

デコレータには引数を渡すことができる。その場合、デコレータ関数はデコレータ関数を返すデコレータ関数になる。

def def_y(y):
    def gen_func_1arg(func):
        def func_1arg(x):
            return func(x, y)
        return func_1arg
    return gen_func_1arg

@def_y(15)
def func_x(x, y):
    print(x + y)
    return 1

func_x(4)

…②

これで、15をデコレータ関数から引きはがすことができた。 しかし、func_x の定義からは引きはがせない。

def gen_func_1arg(func, y):
    def func_1arg(x):
        return func(x, y)
    return func_1arg

def func_x(x, y):
    print(x + y)
    return 1

func_x = gen_func_1arg(func_x, 15)
func_x(4)

…③

デコレータを使わずにこのようにすることで要求を満たすことができた。

何をしたか。①に舞い戻って、デコレータ関数 gen_func_1argfunc_x に適用する手順を func_x = gen_func_1arg(func_x, 15) という形で冗長に書いた。それに伴って、デコレータ関数は関数と固定する変数の2つを引数に取る形に変更してシンプルにした。

def gen_func_x(y):
    def func_x(x):
        print(x + y)
        return 1
    return func_x

func_x = gen_func_x(15)
func_x(4)

…④

スコープの関係を使うと、このようにすることもできる。func_x(x, y)のyは一つ外側の関数 gen_func_x のスコープで与えられる。この書き方にすると、y の固定だったり、xの固定だったり、yを加工してから固定だったり、といった作業が必要な場合に func_x(x, y) が使いまわせないので、その場合は③が有用だと思う。

参考: