問題意識
たとえば
greeting = "My name is {name}, inmate number {number:04d}"
のような文字列を考える。greeting.format(name='Shohei', number=17)のようにすれば、nameあるいはnumberというプレースホルダに明示的に値を与えることもできるし、通常の文字列の代わりにf-stringにすればスコープからnameやnumberといった名前を引っ張ってきてくれる。
せっかくなのでconfig fileなんかから引っ張ってきた設定も、この方式でフレキシブルにフォーマットできるようにしたい。
もちろん実際に使うときには
cfg = dict(...)
...
text = greeting.format(**cfg)
のようなかたちで使えば良いけれど、実際にformat()メソッドを呼ぶ前に、cfgの中に対応したキーが想定された型で入っているかをチェックしたい。
具体的には、たとえば上のgreetingの場合、このフォーマット文字列から
-
nameという変数が必要 -
numberという変数が、整数に変換できる型で必要
という情報を引き出したい。
stringモジュールの利用
そこで使うのは、format()メソッドの内部(関連?)実装であるstringモジュールになる。
string.Formatterクラスがフォーマッティングにかかわる諸々の作業を抽象化してくれている。今回の場合、parseメソッドを使う:
>>> import string
>>>
>>> greeting = "My name is {name}, inmate number {number:04d}, cheers!"
>>>
>>> fmter = string.Formatter()
>>> fmter.parse(greeting)
<formatteriterator object at 0x10125b7e0>
>>> for elem in fmter.parse(greeting)
... print(elem)
...
('My name is ', 'name', '', None)
(', inmate number ', 'number', '04d', None)
(', cheers!', None, None, None)
>>>
parseメソッドは、4-tupleのイテレータを返す。各tupleは(literal, field, spec, conversion)の形式になっている:
- literal: プレースホルダの前にあるリテラル文字列
- field: プレースホルダに指定された名前
- spec: フォーマット形式
- conversion: (現段階では、どういうときに出てくるかちょっとよくわからなかった)
その他参考
- PEP 3101 意外と知らないことがあった