デコレータの基本について理解したのでまとめる

Pythonのデコレータに関して、理解していることをまとめてみる。

@を使ったデコレータの書き方はただのシンタックスシュガー (読み書きのしやすさのために導入される構文)。

それでは、何に対するシンタックスシュガーなのか。

まず、以下のようなプログラムがあるとする。

def egg():
    return "egg"

if __name__ == '__main__':
    print(egg()) # egg

実行すると、当たり前だが以下の様な結果になる。

egg

次に、先ほどのファイルにscrambleというメソッドを追加する。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

def egg():
    return "egg"

if __name__ == '__main__':
    scramble_egg = scramble(egg)
    print(scramble_egg()) # scramble egg!

scrambleメソッドは、オブジェクトを1つ受け取り、内部に定義されている_scrambleメソッドを返している。 つまり、scrambleメソッド自体は引数に対して何かを行うことはなく、 ただ内部のメソッドを返しているだけの機能しか持っていない。

返される内部メソッドの_scrambleは、scrambleメソッドが受け取ったの返り値に、 "scramble "と"!"という文字列を装飾(デコレート)して返している。

if __name__ == '__main__'では、scrambleメソッドにeggメソッド渡し、 返り値をscramble_eggという変数に格納している。 scrambleメソッドの返り値は_scrambleのため、scramble_egg変数は _scrambleメソッドの内容と同じ挙動をする。

そのため、ファイルを実行すると以下の様な実行結果になる。

scramble egg!

さて、if __name__ == '__main__'の中ではscrambleメソッドの返り値はscramble_eggという変数に格納していた。 ここで、すでに定義されているeggメソッドにscrambleの返り値を格納してみる。 Pythonはメソッドも変数も全てがオブジェクトのため、こういうことができる。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

def egg():
    return "egg"

if __name__ == '__main__':
    egg = scramble(egg) # この部分と
    print(egg())        # この部分

実行結果は上記と同じscramble egg!

eggに対してscrambleメソッドの返り値を格納しているため、eggメソッドは 元の「"egg"という文字列を返す」という機能が上書きされている。 しかし、ただ上書きされたわけではなく、scrambleメソッドに元の機能を引数として渡しているため、 「"egg"という文字列を返す」機能は、_scramble内で使われることになる。

この状態におけるscrambleメソッドがデコレータとなる。 文字通り別のメソッドを装飾することができる。

これまでの手順を@を使って書き換えると、以下のようなコードになる。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

@scramble
def egg():
    return "egg"

if __name__ == '__main__':
    print(egg()) # scramble egg!