Python bite: 引数丸投げ
Submitted by fujiwara on 2009, March 11 - 5:48pm.
Tagged: python
Python では、 関数定義の引数記述に "*" や "**" を使用することで、キーワード引数や任意引数リストを簡単に使用することができます。例えば:
>>> def func(a, *list):
... print list
...
>>> func(1, 2, 3, 4)
(2, 3, 4)
任意引数リストへの自動的な束縛
ここで Decorator パターン的な実装が必要になった場合、 普通に=何も考えないで実装するなら、 以下のようになるのではないでしょうか?
def wrappee(a, *list, **dic):
print 'wrappee'
print list
print dic
def wrapper(a, *list, **dic):
print 'wrapper'
print list
print dic
wrappee(a, list, dic)
『丸投げ』な Decorator パターン
何も考えて無いだけあって、 この実装は咬まれます。
上記関数定義で、
wrapper(1, 2, 3, 4, x=5, y=6, z=7)
を評価してみましょう。
wrapper
(2, 3, 4)
{'y': 6, 'x': 5, 'z': 7}
wrappee
((2, 3, 4), {'y': 6, 'x': 5, 'z': 7})
{} ※ dic は空
咬まれました
全く思った通りになりません。
wrapper() における wrappee() 呼び出しを、 以下のように変えてみましょう。
wrappee(a, list=list, dic=dic)
名前を指定して束縛してみる
キーワード引数の束縛仕様を考えれば、 薄々駄目な予感はしていますが、一応動かしてみましょう (wrapper 出力は省略)。
wrappee
() ※ list は空っぽ
{'list': (2, 3, 4), 'dic': {'y': 6, 'x': 5, 'z': 7}}
更に悪化
予感通り、やっぱり駄目です。 キーワード引数扱いになるのですから、 辞書が入れ子になってしまうのは当然ですね。
しかし、そこはそれ、 長年の勘が『以下のように直してみ?』と告げるので、 直して実行してみたら、 思ったように動いてくれました。
wrappee(a, *list, **dic)
任意引数リスト/キーワード引数を直接束縛
実行してみると:
wrappee
(2, 3, 4)
{'y': 6, 'x': 5, 'z': 7}
期待通りの結果
当初、先に参照した『Python チュートリアル』にも書かれていないと思っていたのですが、 同僚から「4.7.4 引数リストのアンパック で触れている」 との指摘を受けました。
こんな間近に書いてあったのに見落としていたとは…