#60　書式化演算子%とformatメソッドを比較してみた〜構文と書式設定

## はじめに
以前ブログで書式化演算子%とformatメソッドについて触れる機会がありましたが、当時の記事の本題からは逸れてしまう内容だったため深く取り上げることができませんでした。
<br>
書式化演算子%とformatメソッドは共に「数値や文字列に対して新しい書式を設定し、新しく文字列を作成する」ものですが、書式化演算子%は以前の形式であり、より高機能な書式設定として現在はformatメソッドが使用されています。
<br>
本記事では、以前の形式である書式化演算子%とformatメソッドそれぞれについて取り上げ、その機能について比較してみたいと思います。
<br>
#### 本記事に関連するブログ記事
- [Kibelaの記事をBacklogのWikiへ移行する〜Backlog APIを利用する](https://qiita.com/nxted_sapporo/items/45dfbcbef8882e1d59f3)

## 書式化演算子%について
構文は以下の通りです。

```python
[変換指定子(%)を含む文字列]%(値１, 値２,・・・)
```

そして使用例は以下の通り。

```python
goro = "ハクシ(白紙)に戻そう遣唐使"
year = 894
print("%d年：%s・・・遣唐使の廃止"%(year, goro))
# -> 894年：ハクシ(白紙)に戻そう遣唐使・・・遣唐使の廃止
```

ざっくりと構文のイメージができたでしょうか。
ではもう少し変換指定子について取り上げてみましょう。

### 変換指定子による書式設定
変換指定子の項目を設定することで書式を指定します。
上の例では`%d`や`%s`がこれにあたります。
変換指定子の形式は以下の通り。項目最後の「変換型」のみ必須項目で、残りはすべてオプションです。

```python
%[(マップキー)][変換フラグ][最小表示幅][精度][変換型]
```

各項目の詳細については比較時に取り上げるとして、それぞれの大まかな内容を確認していきましょう。
<br>

**マップキー**

構文の右側が辞書などのマップ型の際に使用します。
マップキーを指定する場合は「( )」で囲んで示します。

```python
# タプル型
[変換指定子(%)を含む文字列]%(値１, 値２,・・・)
print("%d年：%s・・・遣唐使の廃止"%(894, "ハクシ(白紙)に戻そう遣唐使"))
# -> 894年：ハクシ(白紙)に戻そう遣唐使・・・遣唐使の廃止

# マップ型
[変換指定子(%)を含む文字列]%{キー１：値１, キー２：値２,・・・}
print("%(year)d年：%(goro)s・・・遣唐使の廃止"%{"goro":"ハクシ(白紙)に戻そう遣唐使", "year":894})
# -> 894年：ハクシ(白紙)に戻そう遣唐使・・・遣唐使の廃止
```

<br>

**変換フラグ**

変換された値を左寄せに表示したり、変換した数値の先頭に「+」「-」の符号文字をつけたりすることができます。
詳細は後ほど、formatメソッドとの比較時に取り上げます。

<br>

**最小表示幅**

変換にあたり、最低限保証される桁数を指します。
数値、文字列共にデフォルトは右寄せとなり、例えば最小表示幅が5桁で変換文字列が「平城京」だとすると「　　平城京」と表示されます。
こちらも詳細は比較時に確認します。

<br>

**精度**

小数部分の精度(=桁数)を指定します。
デフォルトは6桁で、ドット(.)の後ろに指定したい桁数を示します。

```python
print("%.3f"%(0.1234567)) # 「f」 = 10進浮動小数点数
# -> 0.123
```

<br>

**変換型**

変換指定子で唯一の必須項目です。
これまでの例で出てきたものとしては
d：符号付き10進整数
s：文字列（Pythonオブジェクトをstr()で変換）
f：10進浮動小数点数
がありました。この他の変換型については比較時に取り上げます。

## formatメソッドについて
構文は以下の通りです。

```python
[置換フィールドを含む文字列].format(値１, 値２,・・・)
```

そして使用例は以下の通り。

```python
goro = "ハクシ(白紙)に戻そう遣唐使"
year = 894
print("{}年：{}・・・遣唐使の廃止".format(year, goro))
# -> 894年：ハクシ(白紙)に戻そう遣唐使・・・遣唐使の廃止
```

置換フィールドには数値やキーワードを指定することができます。
そのため、以下のような記述でも上の例と同じ出力とすることができます。

```python
# 数値を指定
print("{0}年：{1}・・・遣唐使の廃止".format(894, "ハクシ(白紙)に戻そう遣唐使"))
print("{}年：{}・・・遣唐使の廃止".format(894, "ハクシ(白紙)に戻そう遣唐使")) # formatの引数順であれば数値の指定は省略可
print("{1}年：{0}・・・遣唐使の廃止".format("ハクシ(白紙)に戻そう遣唐使", 894))

# キーワードを指定
print("{year}年：{goro}・・・遣唐使の廃止".format(year=894, goro="ハクシ(白紙)に戻そう遣唐使"))

# -> 894年：ハクシ(白紙)に戻そう遣唐使・・・遣唐使の廃止
```

formatメソッドでは、変換する書式指定子を設定したい場合、置換フィールドでコロン(:)の後ろに記述していきます。
形式としては以下の通りです。

```python
{[位置を示す数値またはキーワード]:書式指定子}
```

先の例からもわかるように、formatメソッドの書式指定子には必須項目がありません。
省略可能な以下のオプションを組み合わせることで設定していきます。

> [[fill(埋める文字)]align(配置方法)]
[sign(符号)]
[#(別形式)]
[0(ゼロパディング)]
[width(最小フィールド幅)]
[grouping_option(数値の区切り文字)]
[.precision(小数部分の精度)]
[type(値の表現型)]

引用：https://www.javadrive.jp/python/string/index24.html

<br>

では実際に書式演算子%の書式設定と比較してみましょう。

## 書式設定の比較
**変換型と値の表現型**

まずは書式演算子%の必須項目である変換型とformatメソッド[type(値の表現型)]を見てみましょう。

|出力|コード|項目|←変換型／type→|項目|コード|出力|
|----|----|----|:--:|----|----|----|
|30|`print("%d"%(30))`|符号付き10進整数|d|10進数|`print("{:d}".format(30))`|30|
|36|`print("%o"%(30))`|符号付き8進整数|o|8進数|`print("{:o}".format(30))`|36|
|1e|`print("%x"%(30))`|符号付き16進整数|x|16進数|`print("{:x}".format(30))`|1e|
|-|-|-|b|2進数|`print("{:b}".format(30))`|11110|
|3.000000e+01|`print("%e"%(30))`|指数表記の浮動小数点数|e|指数表記|`print("{:e}".format(30))`|3.000000e+01|
|30.000000|`print("%f"%(30))`|10進浮動小数点数|f|固定小数点数表記|`print("{:f}".format(30))`|30.000000|
|-|-|-|%|数値を100倍してfで%表示|`print("{:%}".format(30.123456789))`|3012.345679%|
|HEY城京|`print("%s城京"%("HEY"))`|Pythonオブジェクトをstr()で変換する文字列|s|文字列|`print("{:s}城京".format("HEY"))`|HEY城京|
|'HEY'城京|`print("%r城京"%("HEY"))`|Pythonオブジェクトをrepr()で変換する文字列|r|-|-|-|
|'HEY'城京|`print("%a城京"%("HEY"))`|Pythonオブジェクトをascii()で変換する文字列|a|-|-|-|
|H城京|`print("%c城京"%("H"))`|文字一字|c|-|-|-|

formatメソッドでは%表示や2進数の変換が可能となっていることがわかります。

<br>

**最小表示幅**
続いて、書式演算子%の最小表示幅とformatメソッド[width(最小フィールド幅)]を見てみましょう。
書式演算子%はデフォルトで右寄せですが、formatメソッドは文字列のデフォルトが左寄せ、数値のデフォルトが右寄せとなっています。
例）最小表示(フィールド)幅：5桁の時
※左右寄せがわかりやすいように【】で囲っていますが、実際は不要です

|項目|タイプ|コード|出力|
|----|----|----|----|
|書式演算子%|文字列|`print("【%5s】"%("平城京"))`|`【  平城京】`|
|formatメソッド|文字列|`print("【{:5s}】".format("平城京"))`|`【平城京  】`|
|書式演算子%|数値|`print("【%5d】"%(894))`|`【  894】`|
|formatメソッド|数値|`print("【{:5d}】".format(894))`|`【  894】`|

<br>

**精度**
こちらは書式演算子%の精度とformatメソッド[.precision(小数部分の精度)]でほぼ変わらないです。
どちらも、
・小数部分の精度(=桁数)を指定する
・デフォルトは6桁
・ドット(.)の後ろに指定したい桁数を示す
です。

```python
# 書式演算子%
print("%.4f"%(0.1234567))

# formatメソッド
print("{:.4f}".format(0.1234567))

# -> 0.1235
```

<br>

**変換フラグとformatメソッドの書式設定**

最後に、書式演算子%の変換フラグに対応またはさらに高機能となったformatメソッドの書式設定について比較します。

書式演算子%の変換フラグには次のようなものがあります。

> "#" 値の変換に"別の形式" を使う  
"0" 数値型に対してゼロパディングを行う  
"-" 変換された値を左寄せにする  
" " 符号付きの変換で正の数の場合、前に一つスペースを空ける。そうでない場合は空文字になる  
"+" 変換の先頭に符号文字 ('+' または '-') を付ける

引用：https://www.javadrive.jp/python/string/index23.html#section3

<br>

それぞれに対応するformatメソッドの書式設定について、順番に確認していきます。
<br>

 **①"#" 値の変換に"別の形式" を使う**
 
8進数・16進数を示す変換型を指定したときに`#`をつけることで、それぞれを示す接頭辞を表示する「別の形式」となります。
これに加え、formatメソッドでは2進数に変換する際にも使用することができます。

```python
# 8進数　-> 0o36
print("%#o"%(30))
print("{:#o}".format(30))

# 16進数　-> 0x1e
print("%#x"%(30))
print("{:#x}".format(30))

# 2進数　-> 0b11110
print("%#b"%(30))
```

 **②"0" 数値型に対してゼロパディングを行う**
 
最小表示幅で左右寄せの表示となった際に、ゼロパディングを行うことで空白ではなく0を埋めることができます。

```python
# （最小表示幅が5桁を指定）
# 書式演算子%
print("【%05d】"%(894))

# formatメソッド
print("【{:0>5d}】".format(894))

# -> 【00894】
```

formatメソッドでは空白を埋めるのは0に限りません。
[fill(埋める文字)]オプションを使用することで、数値だけでなく文字列に対しても利用することができます。

```python
# fill = 1 (数値)
print("【{:1>5d}】".format(894)) # -> 【11894】

# fill = * (数値)
print("【{:*>5d}】".format(894)) # -> 【**894】

# fill = + (文字列)
print("【{:+>5s}】".format("平城京")) # -> 【++平城京】
```

 **③"-" 変換された値を左寄せにする**
書式演算子%では最小表示幅で右寄せで変換されるものに対して「-」をつけることで左寄せとすることができます。

```python
# （最小表示幅が5桁を指定）
# 書式演算子%
print("【%-5d】"%(894))
print("【%-5s】"%("平城京"))

# formatメソッド
print("【{:<5d}】".format(894))
print("【{:5s}】".format("平城京"))

# -> 【894  】
# -> 【平城京  】
```

[align(配置方法)]オプションを使用するformatメソッドでは左右寄せの他、中央揃えの指定も可能です。

```python
# 左寄せ[<](数値)
print("【{:<5d}】".format(894)) # -> 【894  】

# 右寄せ[>](文字列)
print("【{:>5s}】".format("平城京")) # -> 【  平城京】

# 中央揃え[^] + fill[*] (文字列)
print("【{:*^5s}】".format("平城京")) # -> 【*平城京*】
```


 **④" " 符号付きの変換で正の数の場合、前に一つスペースを空ける。そうでない場合は空文字になる**
 **⑤"+" 変換の先頭に符号文字 ('+' または '-') を付ける**

④⑤はformatメソッドでは[sign(符号)]オプションに対応します。
「-」は書式演算子%では左寄せですが、formatメソッドでは「数値が負の場合のみ符号をつける」となります。

【「+」の時】
書式演算子%：正負で符号をつける
formatメソッド：正負で符号をつける

【「-」の時】
書式演算子%：左寄せ
formatメソッド：負の時のみ符号をつける

【「 」(スペース)の時】
書式演算子%：正の時のみスペース
formatメソッド：正はスペース／負は符号をつける
<br>

**書式化演算子%にはない書式設定**

formatメソッド[grouping_option(数値の区切り文字)]オプションは数値に対して千の位ごとにカンマまたはアンダーバーによって区切ることができます。

```python
print("【{:,d}】".format(123456789))
print("【{:_d}】".format(123456789))

# -> 【123,456,789】
# -> 【123_456_789】
```

## おわりに
今回は書式化演算子%とformatメソッドそれぞれについて取り上げ、その機能について比較してみました。
<br>
当初予定していたよりも文量があったため、今回は機能面での比較のみとしました。
次回は過去ブログで作成したスクリプトの書式化演算子%とformatメソッドを使用した箇所を、統一させた記述にするとどうなるか、formatメソッドをより簡潔にしたフォーマット済み文字リテラルにも触れつつまとめていきたいと思います。
<br>
最後まで閲覧いただきありがとうございます。
<br><br>

参考：

 - https://www.javadrive.jp/python/string/index23.html#section3
 - https://www.javadrive.jp/python/string/index24.html
 - https://docs.python.org/ja/3/library/string.html#formatspec
 - https://coroconlab.com/percent_operator/