CLOVER🍀

That was when it all began.

Pythonでの、文字列書式化の方法をメモする

使おうと思った時に忘れているので、メモとして。

Python 3.6以降には、3つの文字列書式化を実現する方法があります。

7. 入力と出力 — Python 3.6.10 ドキュメント

f-stringはPython 3.6から追加されたものです。また、printf 形式の文字列書式化については、「古い文字列書式設定方法」とされて
いるので、f-stringまたはstr.formatを使った方がよいのでしょう。

printf 形式の文字列書式化のドキュメントにも、そのように書いてあります。

printf 形式の文字列書式化

注釈 ここで述べる書式化演算には様々な癖があり、よく間違いの元になっています (タプルや辞書を正しく表示できないなど)。新しい フォーマット文字列リテラル や str.format() インタフェースの方が間違いにくく、より強力で、柔軟で、さらに拡張可能です。

速度面では、古いスタイルの方が有利な感じも…。

pythonのf-stringとformatとパーセント%の書式の速度の比較 - Qiita

とはいえ、Python 3.6以上であればf-stringでいいのかなぁと思います。使いやすいですし。

というわけで、このあたりをまとめていきます。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic

Pythonは、3.6.9です。

$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

書式化に取り込む値としては、このあたりを用意して試していきます。

>>> my_name = "Taro"
>>> int_value = 15
>>> int_value2 = 1000000
>>> float_value = decimal_value = 195.786235
>>> dic = {"name": "Taro", "age": 15}
>>> from datetime import datetime
>>> now = datetime(2020, 1, 1, 17, 30, 0)

f-string

まずは、f-stringから。

フォーマット済み文字列リテラル(f-string)

f-stringは、「f」または「F」が接頭辞に付いた文字列リテラルです。文字列中に、波括弧{}を使うことで値を埋め込んだり、
フォーマットしたりすることができます。

{}の中は、式として評価されるようです。

シンプルな例は、こちら。

>>> f"Hello {my_name}"
'Hello Taro'

>>> f"int value = {int_value}"
'int value = 15'

>>> f"float value = {float_value}"
'float value = 195.786235'

>>> f"dict = {dic}"
"dict = {'name': 'Taro', 'age': 15}"

>>> f"now = {now}"
'now = 2020-01-01 17:30:00'

辞書のキーを指定したり、オブジェクトのネストした情報にアクセスする場合。

>>> f"name = {dic['name']}, age = {dic['age']}"
'name = Taro, age = 15'

>>> f"year = {now.year}, month = {now.month}, hour = {now.hour}"
'year = 2020, month = 1, hour = 17'

フォーマットを指定する場合は、str.formatと同じものが使えるようです。

書式指定文字列の文法

フィールドの書き方が以下のようになり、

フォーマット済み文字列リテラル(f-string)

replacement_field ::= "{" f_expression ["!" conversion] [":" format_spec] "}"

書式の仕様はこのようになります。

format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]

つまり{変数名(や式):format_spec}のような指定方法となります。

いくつか例を。

### 先頭0埋め
>>> f"int value = {int_value:05}"  # widthを指定
'int value = 00015'

>>> f"int value = {int_value:05d}"  # widthとtypeを指定
'int value = 00015'


### カンマ区切り
>>> f"comma separate = {int_value2:,}"  # grouping_optionを指定
'comma separate = 1,000,000'


### 16進数
>>> f"int value = {int_value:x}"  # typeを指定
'int value = f'

>>> f"int value = {int_value:#x}"  # 別形式(#)とtypeを指定
'int value = 0xf'


### 小数点以下の桁数を指定
>>> f"float value = {float_value:.3f}"  # precisionとtypeを指定
'float value = 195.786'

>>> f"float value = {float_value:010.3f}"  # widthを指定して、0埋めと併用
'float value = 000195.786'


### 日付書式指定
>>> f"now = {now:%Y-%m-%d %H:%M:%S}"  # https://docs.python.org/ja/3.6/library/datetime.html#strftime-strptime-behavior
'now = 2020-01-01 17:30:00'

{}の中は式として評価されるので、演算させることもできます。

>>> f"int value * 2 = {int_value * 2}"
'int value * 2 = 30'

f-string内で{}を使う場合は、{{}}と重ね合わせればOKです。

>>> f"name = {{my_name}}"
'name = {my_name}'

その他、左詰め、右詰め、中央寄せ(align)とかもできたりします。

なお、f-stringは、ネストさせる({}の中に{}を使う)ことも可能です。

>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

最後に、変換フィールドの例にも触れておきましょう。

>>> f"Hello {my_name}"
'Hello Taro'


>>> f"Hello {my_name!r}"  # r変換フィールドを指定
"Hello 'Taro'"

「!r」の場合は、reprを呼び出します。この他、strを呼び出す「!s」、asciiを呼び出す「!a」があります。

指定可能なtype(型)、align、signなどはたくさんあるので、すべてをここで網羅するようなことはしません。あとは、ドキュメントを
適宜参照、と。

f-stringは、これくらいで。

str.format

続いては、str.format。

str.format

基本的にはf-stringと同じですが、いくつか違いがあるようです。

f-stringの時と同じく、シンプルな例を。

>>> "Hello {}".format(my_name)

>>> "int value = {}".format(int_value)
'int value = 15'

>>> "float value = {}".format(float_value)
'float value = 195.786235'

>>> "dict = {}".format(dic)
"dict = {'name': 'Taro', 'age': 15}"

>>> "now = {}".format(now)
'now = 2020-01-01 17:30:00'

{}の中は、インデックスを指定することで置換対象となる変数の指定をコントロールすることができます。

>>> "int value = {1}, float value = {0}".format(float_value, int_value)
'int value = 15, float value = 195.786235'

辞書のキーを指定したり、オブジェクトのネストした情報にアクセスする場合。

>>> "name = {0[name]}, age = {0[age]}".format(dic)
'name = Taro, age = 15'


>>> "year = {0.year}, month = {0.month}, hour = {0.hour}".format(now)
'year = 2020, month = 1, hour = 17'

f-stringと異なり、辞書のキーを指定する時にクォートを加えるとエラーになります。

>>> "name = {0['name']}, age = {0['age']}".format(dic)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: "'name'"

キーワード引数が使えるようなので、こういう指定方法でもよさそうです。

>>> "name = {name}, age = {age}".format(name = dic['name'], age = dic['age'])
'name = Taro, age = 15'

>>> "name = {name}, age = {age}".format(**dic)
'name = Taro, age = 15'

>>> "year = {year}, month = {month}, hour = {hour}".format(year = now.year, month = now.month, hour = now.hour)
'year = 2020, month = 1, hour = 17'

書式指定の例。フォーマットは、

書式指定文字列の文法

に従います。

### 先頭0埋め
>>> "int value = {:05}".format(int_value)  # widthを指定
'int value = 00015'

>>> "int value = {0:05}".format(int_value)  # インデックスも指定
'int value = 00015'

>>> "int value = {:05d}".format(int_value)  # widthとtypeを指定
'int value = 00015'


### カンマ区切り
>>> "comma separate = {:,}".format(int_value2)  # grouping_optionを指定
'comma separate = 1,000,000'


### 16進数
>>> "int value = {:x}".format(int_value)  # typeを指定
'int value = f'

>>> "int value = {:#x}".format(int_value)  # 別形式(#)とtypeを指定
'int value = 0xf'


### 小数点以下の桁数を指定
>>> "float value = {:.3f}".format(float_value)  # precisionとtypeを指定
'float value = 195.786'

>>> "float value = {:010.3f}".format(float_value)  # widthを指定して、0埋めと併用
'float value = 000195.786'


### 日付書式指定
>>> "now = {:%Y-%m-%d %H:%M:%S}".format(now)  # https://docs.python.org/ja/3.6/library/datetime.html#strftime-strptime-behavior
'now = 2020-01-01 17:30:00'

str.formatの{}の中は、式として評価することはできません。

>>> "int value * 2 = {0 * 2}".format(int_value)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: '0 * 2'

{}のエスケープ。

>>> "Hello {{0}}".format(my_name)
'Hello {0}'

フォーマットはf-stringの時と同じなので、左寄せなどのalignも利用できます。

format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]

変換フィールド。

>>> "Hello {}".format(my_name)
'Hello Taro'


>>> "Hello {!r}".format(my_name)  # r変換フィールドを指定
"Hello 'Taro'"

str.formatは、こんなところで。

printf 形式の文字列書式化

最後は、printf 形式の文字列書式化。

printf 形式の文字列書式化

一応押さえておきましょう。

この形式は、文字列中に指定子、文字列の後の%に続けて変数を指定して使用します。

見た方がいい気がします。

>>> "Hello %s" % my_name
'Hello Taro'

>>> "int value = %d" % int_value
'int value = 15'

>>> "float value = %f" % float_value
'float value = 195.786235'

>>> "dict = %s" % dic
"dict = {'name': 'Taro', 'age': 15}"

>>> "now = %s" % now
'now = 2020-01-01 17:30:00'

辞書のキーを指定したり、オブジェクトのネストした情報にアクセスする場合。

>>> "name = %(name)s, age = %(age)d" % dic
'name = Taro, age = 15'

>>> "year = %(year)d, month = %(month)d, hour = %(hour)d" % {"year": now.year, "month": now.month, "hour": now.hour}
'year = 2020, month = 1, hour = 17'

まあ、辞書にしてください、ということですね…。

書式の指定例。

### 先頭0埋め
>>> "int value = %05d" % int_value
'int value = 00015'


### 16進数
>>> "int value = %xd" % int_value
'int value = fd'

>>> "int value = %#xd" % int_value
'int value = 0xfd'


### 小数点以下の桁数を指定
>>> "float value = %.3f" % float_value
'float value = 195.786'

>>> "float value = %010.3f" % float_value
'float value = 000195.786'

こちらは、このくらいで。