pythonにはデコレータ構文が標準で実装されている。デコレータを使うことで、既存の関数に対し簡単に別の機能を付加(デコレート)することができる。
例えば、以下の様なコードを書いたとする。
def cake(): print("cake") cake()
とくれば、もちろん出力は
cake
となる。
次に、新たな関数を以下のように書き加えてみる。
def chocolate(func): print("chocolate") func() @chocolate def cake(): print("cake") cake
出力:
chocolate cake
出力結果を見ると、cake()
の出力の前に呼び出してもいないchocolate()
の出力が表示されている。@chocolate
をくっつけたcake
を呼び出しただけで、 chocolate()
の提供する機能を実行している。これがデコレータ。
もっと言うと、変更後コード中の関数呼び出しcake
は以下と等価とのこと。
cake = chocolate(cake)
すなわち、変数cakeに、関数オブジェクトchocolateが同じく関数オブジェクトであるcakeを引数に取りつつ格納されているとみなせる。cake()
呼び出し時に括弧が外れていたのはそのため。
また当然ながら、chocolate()
内部で引数に取る関数の呼びたしタイミングは任意に決めることができる。
def chocolate(func): print("chocolate") @chocolate def cake(): print("cake") cake
出力:
cake chocolate
ネストも可能。
def jam(func): print("jam") return func def chocolate(func): print("chocolate") return func @jam @chocolate def cake(): print("cake") cake()
デコレータ用の関数で関数を返戻しているのは、ネストした関数を正しく呼び出すため。
例えば以下のようにすると、
def jam(func): print("jam") func() def chocolate(func): print("chocolate") func() @jam @chocolate def cake(): print("cake") cake
以下の様なエラーを吐く。
Traceback (most recent call last): chocolate File "/Users/.../Test/calc/__init__.py", line 14, in <module> cake @chocolate jam File "/Users/.../Test/calc/__init__.py", line 7, in jam func() TypeError: 'NoneType' object is not callable
当然といえば当然で、この時のcakeは
cake = jam(chocolate(cake))
と等価で、中の
chocolate(cake)
は関数呼び出しであり、ひと通りchocolate()
が実行されたあともその返戻値はNoneTypeである。したがって「NoneTypeは関数呼び出しできませんよー」と怒られている。