Python 3.0 Hacks

第10回 argument"s" clinic

この記事を読むのに必要な時間:およそ 7 分

はじめに

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は予約語です。答えはこの記事の一番最後に。

著者プロフィール

保坂範行(ほさかのりゆき)

病気でお休みしていましたが活動再開に向けてウォームアップ中です。Life workと化しつつある趣味のゲーム(backgammon)のためのweb-siteを構築しています。局面のimageを動的に生成して返すサーバをPythonで実装&運用中です。

URLhttp://image.backgammonbase.com/

過去の仕事でいやな思いをした経験からPythonの読みやすさを重視する哲学に深く共感。

個人サイト

URLhttp://www.tonic-water.com/

コメント

コメントの記入