Python のデコレータ式 (2) のつづき
1. デコレートされる関数に引数がある場合を考える
これまで試したデコレータ式は、デコレートされる側の関数が引数を取らなかった。
def D(f):
print u"デコレータが実行された"
return f
# デコレータを適用したときに、関数 D が適用される。
@D
def hoge():
print "hoge"
hoge()
前回は `デコレートする側に引数がある’ 場合について考えた。
def D(arg):
def _(f):
def __():
print "*--" * arg
f()
print "--*" * arg
return __
return _
@D(5)
def hoge():
print "hoge"
hoge()
今回は、デコレートされる関数に引数がある場合を考える。
2. デコレータが受け取る関数の引数を固定しないために、可変引数を使う
デコレートする関数は、デコレートされる関数の引数について予め分からない。どのような型の値が渡されるか未知であり、関数が引数をいくつとるのか知らない。
もし、デコレータの受け取る関数の引数の数が固定されていたら、デコレータは使いにくい。これに対処するには、可変引数を使う。
デコレータについて考える前に、Python の可変引数 の書き方を復習しておく。
def test(*args):
for e in args:
print e
test(1,2,3,"hoge","piyo")
def test2(**keywords):
for k,v in keywords.iteritems():
print k, v
test2(a=100, b=200)
Python では、引数の前にアスタリスクを付けると、可変引数となる。
*args, **keywords
3. デコレートされる関数の引数が一つの場合
最初は、「デコレートされる側の関数の引数が一つ」の場合について考える。
def D(f):
def _(arg):
print "*--" * 10
f(arg)
print "--*" * 10
return _
@D
def hoge(n):
print "hoge" * n
##hoge = D(hoge)
hoge(3)
これを実行すると、
*--*--*--*--*--*--*--*--*--*-- hogehogehoge --*--*--*--*--*--*--*--*—*—*
コードが実行される様子を順に考える。上記コード中のコメントアウトしてある、デコレータ式を使わない場合で考えると分かりやすい。
- デコレータ関数 D に、関数 hoge を渡すと、`_ 関数’ が返される。このとき引数 f に hoge がセットされる。
- hoge(3) の呼出しは、`_(3)’ となり、ネストされた関数が実行される。
追記 (2009.12.22) : 上記の手順 1 で返される `_ 関数’ は、
引数を一つ受け取る関数
として定義されている。デコレートしたときに、何か特別な機能として arg に引数が渡される仕組みがあるわけではない。
4. デコレートされる関数の引数が複数の場合
次に、デコレートされる関数の引数が、複数の場合に対応できるように変更する。
def D(f):
def _(*args):
print "*--" * 10
f(*args)
print "--*" * 10
return _
@D
def hoge(n):
print "hoge" * n
@D
def piyo(a,b):
print "piyo" * a
print "piyo" * b
##hoge = D(hoge)
##piyo = D(piyo)
hoge(3)
piyo(3,5)
実行した結果は、
*--*--*--*--*--*--*--*--*--*-- hogehogehoge --*--*--*--*--*--*--*--*--*--* *--*--*--*--*--*--*--*--*--*-- piyopiyopiyo piyopiyopiyopiyopiyo --*--*--*--*--*--*--*--*--*--*
デコレートする関数におけるネストした関数の引数を
*args
とすることにより、引数の数が異なる hoge(n), piyo(a,b) 関数をデコレートできるようになった。
`_ 関数’ は、
複数の引数を受け取る関数
として定義されている。
5. デコレートされる関数の引数にキーワード引数が含まれる場合
デコレートされる関数の引数に、キーワード引数 が含まれている場合は、次のように記述する。
def D(f):
def _(*args, **keywords):
print "*--" * 10
f(*args, **keywords)
print "--*" * 10
return _
@D
def hoge(n):
print "hoge" * n
@D
def piyo(x,y):
print "piyo" * x
print "piyo" * y
@D
def fuga(a,b,c=8,d=10):
print "fuga" * a
print "fuga" * b
print "fuga" * c
print "fuga" * d
hoge(5)
piyo(5,10)
fuga(1,2)
fuga(1,2,3)
fuga(1,2,c=10)
fuga(1,2,c=10,d=5)
これまでと同じように、デコレートされたときに
複数の引数とキーワード引数を受け取る関数
が返されるように定義する。
6. デコレートする関数、デコレートされる関数に引数がある場合
前回は、デコレートする関数に引数がある場合について考えた。
デコレートする関数と、デコレートされる関数に引数がある場合を定義してみる。デコレータに引数が渡されるので、ネストが一つ増えることに注意する。
def D(n):
def _(f):
def __(*args, **keywords):
print "*--" * n
f(*args, **keywords)
print "--*" * n
return __
return _
@D(15)
def hoge(n):
print "hoge" * n
@D(10)
def fuga(a,b,c=8,d=10):
print "fuga" * a
print "fuga" * b
print "fuga" * c
print "fuga" * d
##hoge = D(15)(hoge)
hoge(10)
fuga(1,2,c=5,d=7)
実行した結果は、
*--*--*--*--*--*--*--*--*--*--*--*--*--*--*-- hogehogehogehogehogehogehogehogehogehoge --*--*--*--*--*--*--*--*--*--*--*--*--*--*--* *--*--*--*--*--*--*--*--*--*-- fuga fugafuga fugafugafugafugafuga fugafugafugafugafugafugafuga --*--*--*--*--*--*--*--*--*—*
7. デコレータを重ねて利用する
最後に、
- デコレートに必要な情報をデコレータの呼出しのときに渡し、
- デコレータを重ねて利用する
と次のようになる。
def D(top, bottom, x, y):
def _(f):
def __(*args, **keywords):
print top * x
f(*args, **keywords)
print bottom * y
return __
return _
@D("-#-", "-#-", 4, 10)
@D("--*", "*--", 3, 9)
@D("-+-", "+-+", 2, 7)
def fuga(a,b,c=8,d=10):
print "fuga" * a
print "fuga" * b
print "fuga" * c
print "fuga" * d
fuga(1,2,c=3,d=4)
結果は、
-#--#--#--#- --*--*--* -+--+- fuga fugafuga fugafugafuga fugafugafugafuga +-++-++-++-++-++-++-+ *--*--*--*--*--*--*--*--*-- -#--#--#--#--#--#--#—#—#—#-
これで、一つのデコレータで様々な装飾を施せるようなった。 ^^
下図に、色分けして見やすくしたコードを示す。
0コメント:
コメントを投稿