HTML タグ付き文字列を動的に生成する実装のリファクタリング

1年前に開発したアプリケーションの機能拡張に伴い、徹底的にリファクタリングしてやろうと気合を入れて改修中です。

そんな中、HTML タグ付き文字列を動的に生成する - forest book で書いた処理を、いま見返すと見た目があまりよろしくありませんね。もうちょっと格好良くリファクタリングしてみました。

def get_html(tag, **kw):
    """
    >>> get_html("anchor", url="http://bit.ly/bT2kfY", name="forest book")
    '<a href=http://bit.ly/bT2kfY >forest book</a>'
    >>> get_html("input", type="text", id="blog_comment", name="comment")
    '<input type=text id=blog_comment name=comment />'
    """
    func = {}
    func['anchor'] = get_anchor_tag
    func['input'] = get_input_tag
    return func[tag](**kw)

def get_anchor_tag(**kw):
    """
    >>> get_anchor_tag(url="http://bit.ly/bT2kfY", name="forest book")
    '<a href=http://bit.ly/bT2kfY >forest book</a>'
    >>> get_anchor_tag(url="http://bit.ly/bT2kfY",
    ...                name="forest book", attr="value=1")
    '<a href=http://bit.ly/bT2kfY value=1>forest book</a>'
    """
    kw.setdefault("attr", "")
    return "<a href=%(url)s %(attr)s>%(name)s</a>" % kw

Python 2.6 以上は format() メソッドを使った記述の方が良いそうです。

def get_input_tag(**kw):
    """
    >>> get_input_tag(type="text", id="blog_comment", name="comment")
    '<input type=text id=blog_comment name=comment />'
    >>> get_input_tag(type="text", id="blog_comment", name="comment",
    ...                     attr='style="width:100px"')
    '<input type=text id=blog_comment name=comment style="width:100px"/>'
    """
    kw.setdefault("attr", "")
    return "<input type={type} id={id} name={name} {attr}/>".format(**kw)

また Python 2.5 以上で collections.defaultdict を使うと、デフォルト値を生成するファクトリ関数を設定できます *1

def get_anchor_dd_mapping(d):
    """
    >>> from collections import defaultdict
    >>> get_anchor_dd_mapping(defaultdict((lambda: ""),
    ...                       url="http://bit.ly/bT2kfY", name="forest book"))
    '<a href=http://bit.ly/bT2kfY >forest book</a>'
    """
    return "<a href=%(url)s %(attr)s>%(name)s</a>" % d

def get_anchor_dd_format(d):
    """ 
    >>> from collections import defaultdict
    >>> get_anchor_dd_format(defaultdict((lambda: ""),
    ...                      url="http://bit.ly/bT2kfY", name="forest book"))
    '<a href=http://bit.ly/bT2kfY >forest book</a>'
    """
    return "<a href={url} {attr}>{name}</a>".format(
            url=d["url"], attr=d["attr"], name=d["name"])

この例は setdefault() でも実装できる処理を置き換えているだけなので、あまり良い例ではありません。もっと詳しく知りたい方は defaultdict の使用例 を参考にしてください。

format() メソッドへ渡すときに、以下のように ** 引数を使ったりすると横着するなと怒られます。

...
    return "<a href={url} {attr}>{name}</a>".format(**d)

**********************************************************************
File "operate_html_tag.py", line 50, in operate_html_tag.get_anchor_dd_format
Failed example:
    get_anchor_dd_format(defaultdict((lambda: ""),
                         url="http://bit.ly/bT2kfY", name="forest book"))
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib/python2.6/doctest.py", line 1241, in __run
        compileflags, 1) in test.globs
      File "<doctest operate_html_tag.get_anchor_dd_format[1]>", line 2, in <module>
        url="http://bit.ly/bT2kfY", name="forest book"))
      File "operate_html_tag.py", line 54, in get_anchor_dd_format
        return "<a href={url} {attr}>{name}</a>".format(**d)
    KeyError: 'attr'
**********************************************************************