式評価機能付 Template
式評価機能があると便利かもしれません。
tags: | python, tips, experiment, template, string, eval |
---|---|
created: | 2007-08-05T20:24:24 |
式評価機能があると便利かもしれません。 標準テンプレートクラスを拡張してみた。
標準テンプレートクラスについて
Python2.4から標準モジュール「string」にある「Template」クラスは、
- 「$」プレフィクスで辞書にアクセスできる。
- 辞書の値を文字列に変換してくれる。
などの特徴をもち、「モジュラ(%)演算子」による書式化よりも簡潔に記述できるメリットがありました。
しかし!、「 Cheetah 」に触れたことがあればこの機能でも不十分に感じるでしょう。
そう。式や関数呼び出しもテンプレートソースに記述したいのです。
かといって、「 Cheetah 」の導入までは必要がないとか、 「 Pure Python 」をキープしたいと思うなら、以下の内容を試して見てください!
ソースコード
# -*- encode: utf-8 -*- import re as _re import string class Template(string.Template): pattern = _re.compile(r"""\\$(?: (?P<escaped>\\$)| (?P<named>[a-zA-Z_]+[a-zA-Z0-9_().]*)| {(?P<braced>[^}]+)}| (?P<invalid>) )""", _re.VERBOSE) def __init__(self, template): string.Template.__init__(self, template) def _miss_point(self, mo): i = mo.start() whole = self.template.splitlines(True) lines = self.template[:i].splitlines(True) if not lines: colno = 1 lineno = 1 else: colno = i - len(''.join(lines[:-1])) lineno = len(lines) return (whole[lineno-1].strip(), lineno, colno) def eval_substitute(self, *args, **keys): if len(args) > 1: raise TypeError('Too many positional arguments') if args[0:]: keys.update(args[0]) def convert(mo): named = mo.group('named') or mo.group('braced') if named is not None: #print named try: value = eval(named, keys) except Exception, ex: line, ln, col = self._miss_point(mo) message = str(ex) message += ' in line, %(ln)d, col %(col)d\n%(ln)d:"%(line)s"' % locals() raise ValueError(message) return '%s' % value if mo.group('escaped') is not None: return self.delimiter if mo.group('invalid') is not None: self._invalid(mo) raise ValueError('Unrecognized named group in pattern', self.pattern) return self.pattern.sub(convert, self.template)
利用例
従来のTemplateを継承しているので、従来の使い方も出来ます。
付け加えたのは、「eval_substitute」 メソッドです。
引数の仕様は「string.Template.substitute」とおなじです。
>>> from template import Template
>>> def mm(a):
... return a*2
...
>>> a1 = 12000
>>> print Template('hoge : mm(a1)[m] = ${'$'}{mm(a1)/1000.}[km]').eval_substitute(vars())
hoge : 24000[m] = 24.0[km]
このように式や算術式がそのまま使えます。インスタンスやメソッドも使えるはず。 一応記述ミスも場所がわかるような例外を投げるようにしてあります。
例えば関数「mm」が未定儀の場合。
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "template.py", line 51, in eval_substitute
return self.pattern.sub(convert, self.template)
File "template.py", line 44, in convert
raise ValueError(message)
ValueError: name 'mm' is not defined in line, 1, col 9
1:"hoge : mm(a1)[m] = ${'$'}{mm(a1)/1000.}[km]"
こんなかんじです。