**表記は便利だ

さて、この仕組みはどうやって実現されているかというと、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}} みたいな値になっているわけだ。

関数のパラメータを辞書に詰め込んで取り出したり、それをまたバラして関数を呼び出したり。なるほどこうやって使うと便利なんだなあ。