Python のデコレータ式 (1) のつづき
1. 前回のデコレータ式の復習
前回は、引数のないデコレータ式について試した。今回はデコレータ式に引数がある場合について考える。
最初に、引数のないデコレータについて復習する。
def D(f): def _(): print "*--" * 10 f() print "--*" * 10 return _ @D def hoge(): print "hoge" ##hoge = D(hoge) hoge()
コメントアウトしてあるコードは、デコレータ式を使わない場合の書き方。デコレータ式は、このシンタクティックシュガー。
デコレートしたときに、関数 D が実行され、デコレートした関数をラップした関数が返される。上記では、デコレートされた後、関数 hoge は、hoge 関数がセットされた、関数 D にネストされた `_ 関数’ を指す。
2. デコレータに引数がある場合
次に、デコレータに引数がある場合を考える。
デコレータに引数を渡せると、デコレートの仕方をカスタマイズできるようになる。上記の例で考えると、出力される “hoge” を囲む
*--*--*--*--*—
の長さを、引数によって指定できるようになる。
デコレータ式を使わない場合で考える
では、上記のデコレートする関数 D に引数を与えたい場合、どうすればいいのだろう?
デコレータ式を使わない関数の呼出しで考えてみる。例えば、以下のようにデコレートする関数 D を呼び出し、引数を与えることができると想定する。
hoge = D(5)(hoge) hoge()
これは、デコレート関数 D は引数として `5’ を受け取り、 hoge 関数をラップしている。
この場合の呼出しを順に考えると、
- 関数 D に 5 を与える。
- 上記の関数呼び出しの結果として返される関数に、関数 hoge を与える。
- 関数 hoge を呼び出す。
の 3 段階となっている。
そのため、デコレート関数に引数を与えない場合の関数定義よりも、一段多くネストした関数が必要となる。
よって、以下のように記述すれば良い。
def D(arg): def _(f): def __(): print "*--" * arg f() print "--*" * arg return __ return _ def hoge(): print "hoge" hoge = D(5)(hoge) hoge()
(今回も適当な関数名が思い付かなったので、返される関数名をアンダーバーとした。)
実行される順を考えると、
- 最初に、デコレート関数 D に 5 を与えることにより、D(arg) が呼出され、`_ 関数’ が返される。
- 次に、`_ 関数’に hoge 関数 を与えることにより、_(f) が呼出され `__ 関数’ が返される。
この 2 段階の呼出しによって、最初に引数 `5’ がセットされ、次に hoge 関数がセットされたことになる。最後に、hoge 変数はデコレータ関数の一番奥にネストされた `__ 関数’ を指した状態になる。
デコレータ式を使う場合
上記をデコレータ式で書き直すと、
def D(arg): def _(f): def __(): print "*--" * arg f() print "--*" * arg return __ return _ @D(5) def hoge(): print "hoge" hoge()
デコレータ式を使わない場合と比べると、記述はシンプルになった。しかし、元の形を知らなければ、デコレータ関数 D との対応が全くわからない。デコレータ式は、あくまでも syntactic sugar 。
3. 複数のデコレータを適用する
次に、デコレータ式を複数適用する場合について考える。
最初は、引数のないデコレータ式を二つ重ねる例を試す。例えば、デコレータ関数 D に加えて、デコレータ関数 C を適用したい場合、次にように書く。
def C(f): def _(): print "-#-"*10 f() print "-#-"*10 return _ def D(f): def _(): print "--*"*10 f() print "*--"*10 return _ @C @D def hoge(): print "hoge" ##hoge = C(D(hoge)) hoge()
実行した結果は、
-#--#--#--#--#--#--#--#--#--#- --*--*--*--*--*--*--*--*--*--* hoge *--*--*--*--*--*--*--*--*--*-- -#--#--#--#--#--#--#--#--#--#-
コメントアウトしてある部分が、デコレータ式を使わない方法。
実行結果を見ると、上の方に書かれているデコレータ式が、外側になることがわかる。
4. 引数のあるデコレータ式を複数適用する
最後に、引数のあるデコレータ式を、複数適用する方法を書く。
def C(arg): def _(f): def __(): print "-#-" * arg f() print "-#-" * arg return __ return _ def D(arg): def _(f): def __(): print "-—*" * arg f() print "*—-" * arg return __ return _ @C(10) @D(5) def hoge(): print "hoge" ##hoge = C(10)(D(5)(hoge)) hoge()
実行結果は、
-#--#--#--#--#--#--#--#--#--#- --*--*--*--*--* hoge *--*--*--*--*-- -#--#--#--#--#--#--#--#--#--#-
ここまで来ると、ネストした関数の見た目が複雑になった。 (@_@;)
しかし、結局、基本的には、
デコレータ関数1(引数)(元の関数)
となっており、これがネストしているだけ。
デコレータ関数2(引数)(デコレータ関数1(引数)(元の関数))
Python のデコレータ式 (3) につづく…
0コメント:
コメントを投稿