Pythonのフォーマット文字列構文についてprintfからf-stringsまで振り返ってみた

Python2以降でのフォーマット文字列について、printfからf-stringsまで用例を交えて振り返ってみました。
2020.01.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Pythonで文字列内に変数展開をする際にformat()を使っていましたが、以下のようなコードを見て、3.6以降からf-stringsという仕組みが導入されていたことを知りました。

path.glob(f'middle/**/{account_id}/.*.csv.gz')

f-stringsで新しい書き方があることを知り、Python2にて用いていたフォーマットを含め、温故知新としてみました。

Python2での文字列フォーマット

printf形式

%を囲む形で、左側にフォーマット、右側に展開する値を置きます。

組み込み型 — Python 3.8.1 ドキュメント

>> "%d %s %d" % (1, "second", 3)
'1 second 3'

%dは符号付き10進整数、%sは文字列となります。使い方によっては、

val_temp = "%d %d"
>> "%d %s %d" % (1, val_temp, 3)
'1 %d %d 3'

のように、条件に応じてのテンプレート切り替えのような扱い方もできます。

10進浮動小数点数の桁を扱う場合は、%fを使用します。

>> "[%-6.2f][%06.2f]" % (1.234, 1.234)
'[1.23  ][001.23]'

1桁目で文字幅を、小数点以下の桁をピリオド以下で指定し、-をつけると左詰めです。06.2と頭に0をつけると、余幅を0埋めする形にて6桁にします。

辞書型を用いるとkeyにて値の位置を指定可能になります。

>> "%(b)d %(a)s" % {"a": "two", "b": 1}
'1 two'

string.format()

2.6から使用可能です。printfでの出力で頻繁に起こっていた、正常に出力されない等のトラブルを抑えやすくなります。

>> "https://dev.{0}.jp/".format('classmethod')
'https://dev.classmethod.jp/'
>> "https://{1}.{0}.jp/".format('classmethod', 'dev')
'https://dev.classmethod.jp/'

インデックスを指定し、該当の引数を渡していきます。

キーワード指定も可能です。

>> 'https://{domain}{path}'.format(domain='dev.classmethod.jp', path='/author/')
'https://dev.classmethod.jp/author/'

キーワード指定を辞書型にした場合はアンパックを用います。

>> value = {"domain": 'dev.classmethod.jp', "path": '/author/'}
>> 'https://{domain}{path}'.format(**value)
'https://dev.classmethod.jp/author/'

f-strings

string.format()よりも簡潔に書ける方法です。Python 3.6から利用可能です。

PEP 498 -- Literal String Interpolation | Python.org

2. 字句解析 — Python 3.8.1 ドキュメント

事前に変数へ値を収めておくと、テンプレート内文字列にて変数名を指定した場合に自動展開されます。

>> members_site_path = '/services/members/'
>> f'https://classmethod.jp{members_site_path}'
'https://classmethod.jp/services/members/'

printfのように0埋めや幅寄せ(右/中央/左)も可能です。

>> value = 54321
>> f'0mapping > {value:010}'
'0mapping > 0000054321'
>> service_1 = 'Build'
>> service_2 = 'Deploy'
>> service_3 = 'Pipeline'
>> f'AWS Code{service_1:!<16}'
'AWS CodeBuild!!!!!!!!!!!'
>> f'AWS Code{service_2:!^16}'
'AWS Code!!!!!Deploy!!!!!'
>> f'AWS Code{service_3:!>16}'
'AWS Code!!!!!!!!Pipeline'

展開する変数に対して関数も利用可能です。

>> rows = ['1', '2', '3', '4']
>> f'rows count:{len(rows)}'
'rows count:4'

あとがき

f-stringsは、最初に見たコードではファイルパスが指定されていたため、「ファイルハンドラもここまで手軽になったのか」と勘違いしていました。事前に宣言した変数名をテンプレート内にkeyとして指定するだけで展開される手軽さは手放せなくなると思います。

利用されているPythonのバージョンによって扱える記述方法が変わってきますが、f-stringsを用いるためにPython2.xからの移行バージョンをPython3.6以降にするのも一つの手かもしれません。