はじめに
Pythonでは関数に引数を不定個数渡し,それらをタプルや辞書として参照できる機能があります。かなり便利な機能で,すっきり書く上で使いでがあります。しかしながら,引数を順番で指定する機能とともに利用されることで,人間が間違えてバグを埋め込む可能性を増やしていました。これに対して3.0では引数の名前を明示的に指定しなければ束縛できないkeyword only argumentという機能が導入されました。
Pythonにおける引数の渡し方
まずは知識の確認と,それを持ち合わせていない方のための下準備です。「引数をタプルや辞書として参照する,タプルや辞書を渡し引数に展開させる」ことについて,2.xのPythonを使って説明したいと思います。
引数をタプルや辞書として参照する
「タプル(tuple)として参照する」とは,リスト1に挙げる例のようなものを指します。2つ以上の引数をfooに渡した場合,それらはargsという名前のtupleとして参照され,fooの内部で使うことができます。引数は前から順番に使われていって,余ったものが全てargsに行く(tupleに参照される)という作りになっています。
リスト1 tuple sample
In [1]: def foo(a, *args):
...: print args
...:
...:
In [2]: foo(1, 2, 3, 4)
(2, 3, 4)
In [3]: foo(1)
()
同様に「辞書として参照する」場合は,リスト2のようにkeyを指定して引数を渡すと,それらが全て辞書に格納されます。注意すべき点は最後の a=1 を渡したケースで,仮引数のaによって使われてしまい,**kwに残らない点です。
リスト2 dict sample
In [4]: def bar(a, **kw):
...: print kw
...:
...:
In [5]: bar(1)
{}
In [6]: bar(1, b=2, c=3, d=4)
{'c': 3, 'b': 2, 'd': 4}
In [7]: bar(b=2, c=3, d=4, a=1)
{'c': 3, 'b': 2, 'd': 4}
タプルや辞書を渡し引数に展開させる
今度は逆にtupleが展開されて仮引数に束縛されるケースです。リスト3の例で,tuple xsの内容が前から順番にa, b, cに束縛されているのがわかると思います。
リスト3
In [9]: def buzz(a, b, c):
...: print a, b, c
...:
...:
In [10]: xs = (1, 2, 3,)
In [11]: buzz(*xs)
1 2 3
最後は辞書が展開されて仮引数に束縛されるケースです(リスト4)。リスト3の例で出てきたbuzz関数に辞書ysを渡してみます。
リスト4
In [12]: ys = {"c":1, "b":2, "a": 3}
In [13]: buzz(**ys)
3 2 1
組み合わせてみる
これらを組み合わせて使うことができます。
リスト5
In [14]: foo(*xs)
(2, 3)
この場合はxsの先頭から順番に要素が引数に割り当てられ,残りがargsに詰められるので,このような結果になります。
リスト6
In [15]: bar(**ys)
{'c': 1, 'b': 2}
同様に辞書ysの場合は,辞書の中からkeyにマッチする仮引数が割り当てられ,残ったitemが辞書kwに詰められます。
辞書とtupleの両方渡すことも可能ですが,仮引数はまず先にtupleを用いて填めていくのでxs, ysの両方をbuzzに渡すことはできません。リスト7のようにaが衝突していしまいます。
リスト7
In [16]: buzz(*xs, **ys)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
TypeError: buzz() got multiple values for keyword argument 'a'
ここまでのおさらいのための簡単なパズル
次のfを,assertに成功するように呼んでください。
def f(**kw):
assert kw["class"] == 1
- ※ pythonでclassは予約語です。答えはこの記事の一番最後に。

