しぷぜん

しぷぜん

素人プログラマなおいがStep Zero to Oneしていくブログ

Pythonでよく忘れる文字と数字の変換に関するあれこれ(format関数)

にほんブログ村 IT技術ブログ Pythonへ

こんにちは、なおいです。

Pythonを触っていると文字列の出力(ファイル出力を含む)でよくフォーマットが問題で出力できないとかで悩んで、検索しているので備忘録シリーズその2を書きます。

 

.format()関数を使った変換

これはよくPythonの事が書かれた本のprint内の型変換で使われているのを見かけます。私は何度も書いてますがC#erでこれに近い書き方に慣れ親しんでいるので、この変換方法は結構好きでよく使ってます。

基本ルール
'文字列 {}'.format(文字列化したい変数など,キーワード引数)

 {} で区切られた置換フィールドに後ろの関数で指定した変数などの演算結果をstring型にして返します。

 

変数の実例をいくつか

本家のドキュメントで使われている例を使って、解読していきます。

 

1.位置引数を使ったアクセス
'{0}, {1}, {2}'.format('a', 'b', 'c')
→'a, b, c'
'{2}, {1}, {0}'.format('a', 'b', 'c')
→'c, b, a'
'{2}, {1}, {0}'.format(*'abc')
→'c, b, a'
'{0}{1}{0}'.format('abra', 'cad')
→'abracadabra'

これは、基本に近いからわかりやすいですね。基本ルールは入れたい変数が一つだったので引数がなかったけど、複数の変数を入れたくなったからナンバリングをしてるってだけですね。

f:id:ct-innovation01:20170911163842p:plain

引数は0から始まっていることだけ注意すれば、1つ目と2つ目の例からformat内の前から0,1,2と対応していることも分かるし、4つ目から同じものを二回呼び出すことも問題なくできることが分かります。

で、気になったのは3つ目formatには一つの変数しか入ってないし、謎の「*(アスタリスク)」があります。

 アスタリスクはアンパックするという指示みたいです。ここでいう一つの文字列'abc'を'a'と'b'と'c'の3つに分けて引き渡すという事のようです。つまり、2つ目の別表記と考えていいかなと思います。(知りませんでした。)

 

2.名前を使ったアクセス
'Coordinates: {lati}, {longi}'.format(lati='37.24N', longi='-115.81W')
→'Coordinates: 37.24N, -115.81W'
coord = {'lati': '37.24N', 'longi': '-115.81W'}
'Coordinates: {lati}, {longi}'.format(**coord)
→'Coordinates: 37.24N, -115.81W'

これもそこまで難しくないですね。1.のナンバリングだったものの代わりに名前を付けたってだけですね。図示する必要もないかな?

ただここでも出ましたアンパック!しかも今回は「**(アスタリスク2個)」ここは詳細省きますが、辞書型のものをよしなにアンパックしてくれると・・・なんかアンパックはそれだけで記事書けそうなのでそういうものとしてスルーします。

 

3.引数の属性へアクセス
c = 3-5j
('The complex {0} is real part {0.real} and imag part {0.imag}.').format(c)
→'The complex (3-5j) is real part 3.0 and imag part -5.0.'

これ知りませんでしたが便利ですね!

今回cはcomplex型を利用しているのですが、complex型にはrealとimagいうプロパティが存在しています。そのプロパティに対して普通にアクセスができるみたいです。

今までわざわざ別の変数に入れてから表示してましたよ。。。

 

4.引数の要素へアクセス
coord = (3, 5)
'X: {0[0]};  Y: {0[1]}'.format(coord)
→'X: 3;  Y: 5'

これも知りませんでした・・・セットやリストの要素にもindexでアクセスできるみたいです。この方法ってここまで出来ること多かったんだなって驚いてます。

 

キーワード引数の実例をいくつか

こちらも本家の例を参考に解読していきます。 変換時に何か追加処理を行いたい時にこのあたりを用いれば、いろいろできますね。よく使われるのは、小数点以下何桁まで0埋めするとか正負を付けるとかですかね。

 

1.出力するテキスト幅を指定する
'{:30}'.format('aligned')
→'aligned                       '
'{:<30}'.format('left aligned')
→'left aligned                  '
'{:>30}'.format('right aligned')
→'                 right aligned'
'{:^30}'.format('centered')
→'           centered           '
'{:*^30}'.format('centered')
→'***********centered***********'

{}内に「:(コロン)」を書く事でここからキーワード引数だという事を示しているっぽいですね。さらに、添え字を付けずに数字を入れた場合はテキスト幅を指定する。前に「<,>,^」のどれかを入れると文字列の配置位置を決められる。さらに前にcharを入れるとその指定charで埋めてくれるようです。

では、下のを試してみます。ちょっと意地悪でテキスト幅を偶数で指定して対象を文字を1文字にしたらどこに行くのかの実験も兼ねて、

'{5:+^10}'.format(1,2,3,4,5,6,7,8)
→'++++6+++++'

結果はこんな感じで、5文字目に置かれました。頭の片隅に入れておこう・・・

f:id:ct-innovation01:20170912095657p:plain

赤枠で5個目を使ってねと指示し、コロンで区切った後ろで「+」で埋めてね。あ、指定した文字は真ん中ね?でテキスト幅は10文字で!という命令ということですね。理解した。

 

2.基数を指定する(基数の接頭辞付での出力含む)
"int: {0:d};  hex: {0:x};  oct: {0:o}".format(42)
→'int: 42;  hex: 2a;  oct: 52'
"int: {0:d};  hex: {0:#x};  oct: {0:#o}".format(42)
→'int: 42;  hex: 0x2a;  oct: 0o52'

キーワード引数は、さっきと同様で「:(コロン)」からスタートしてますね。

あとは、なぜ「d,x,o」なのかぐらいですかね。多分10進数、8進数、16進数の英語「decimal,Octal,Hexadecimal」の略だと思いますが、調べます。

 

・・・・まあ、予想どおりでした。

あ、あとbを指定すると2進数でも表示できるようです。ここは、そこまで問題なさそうなのでこれでおしまい。

 

3.正負と表示桁数を指定する
'{:+f}; {:+f}'.format(3.14, -3.14)
→'+3.140000; -3.140000'
'{: f}; {: f}'.format(3.14, -3.14)
→' 3.140000; -3.140000'
'{:.2f}; {:.2f}'.format(3.14, -3.14)
→'3.14; -3.14'

fは固定小数点を示す(Fixed-point)なのかな?調べても分かりませんでした。

それより覚えとかなきゃいけないのは「f」だけ指定すると小数点以下6桁表記されてしまう事ですかね。デフォルトが6桁だそうで、好きな桁数にしたい場合は「f」の前に表記したい桁数を3つ目のように「.2」を付与する必要があります。

では、表示桁数を削る時に気になる。四捨五入なのか切捨てなのかを検証。

'{0:.3f}:{0:.4f}'.format(0.123456)
→'0.123:0.1235'

 四捨五入を使ってるみたいですね。頭の中整理できてきた気がする。

 

4.1000の位にカンマを打つ
'{:,}'.format(1234567890)
→'1,234,567,890'

これは言うことないです。これ以上でも以下でもない。。。。

 

5.パーセント表記にする
points = 19.5
total = 22
'Correct answers: {:.2%}'.format(points/total)
→'Correct answers: 88.64%'

これもあんまり言うことないです。あえて何か言うとすれば、勝手に%にしてくれるので、100倍とかしなくていいのが手間がかからなくて嬉しいっていう事ぐらいですかね?検証してないですけど、希望的観測でこれも四捨五入なんじゃないかなと判断してます。

 

6.それぞれの型特有の表記にする
import datetime
d = datetime.datetime(2010, 7, 4, 12, 15, 58)
'{:%Y-%m-%d %H:%M:%S}'.format(d)
→'2010-07-04 12:15:58'

これは都度調べるしかないのかな・・・って感じです。頻出そうな時間に関してのみ例があったので一応載っけときます。

 

最後に使った本と今からでも欲しい本を乗っけて終わります。