**表記は便利だ
さて、この仕組みはどうやって実現されているかというと、turbogears/widgets/forms.py の CompoundInputWidget.params_for(self, item, **params)で実装されている。このメソッド自体はTableFormのテンプレートから呼び出される。こんな感じになっている(抜粋)。
<tr py:for="i, field in enumerate(fields)" class="${i%2 and 'odd' or 'even'}" > <th> <label class="fieldlabel" for="${field.field_id}" py:content="field.label" /> </th> <td> <span py:replace="field.display(value_for(field), **params_for(field))" /> </td> </tr>
「py:replace="field.display(value_for(field), **params_for(field))"」の部分がキモで、**params_for(field)の呼び出しによって、「params_for(field)の戻り値の辞書を、キー=値に展開して引数として渡す」ということになる。これはPythonの言語仕様なんだけど、まだ慣れてないので直感的にうまく把握できない。
params_for()の実装は、retrieve_params_by_path() を呼び出している。retrieve_params_by_path()の実装は、こう。
def retrieve_params_by_path(params, path): if not path: return None else: if not isinstance(path[0], tuple): path = adapt_path(path) for name, index in path: params_to_parse = params.copy() params = {} for k,v in params_to_parse.iteritems(): if isinstance(v, dict): if name in v: params[k] = v[name]←←←←←←←←←←←←←① if index is not None: if isinstance(params[k], list): try: params[k] = params[k][index] except IndexError: params[k] = None else: params[k] = None return params
込み入っているんだけど、①の部分で、kは引数名(上のvalueとかattrsとか)、vはkの値の辞書で、nameはWidgetのnameなので、「value={'input1':'XYZ'}からk='value'、name='input1'、v[name]='XYZ'、しかるにparams['value']='XYZ'」という処理をしている。最終的にこのparamsを返すわけだが、そのときparamsは {'value':'XYZ', 'attrs':'{'readonly':True}} みたいな値になっているわけだ。
関数のパラメータを辞書に詰め込んで取り出したり、それをまたバラして関数を呼び出したり。なるほどこうやって使うと便利なんだなあ。