かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Python3 文字列中に変数展開したい

1. %演算子

変換指定子 フォーマット型
%f 浮動小数点数
%d 整数
%s 文字列
%x 16進数
%o 8進数
%% %
print( 'Hello %s!' % 'World' )
# => Hello World
area = 3.14 * 5 * 5
print( '円の面積は%f平方cm' % area )
# => 円の面積は78.500000平方cm
cat = 3
dog = 6
print( 'cats are %d, dogs are %f' % (cat, dog) )
# => cats are 3, dogs are 6.000000

2. str.format()

str.format(*args, **kwargs)
置換フィールドは位置引数のインデックスナンバー、または、キーワード引数の名前を含みます。返り値は、それぞれの置換フィールドが対応する引数の文字列値で置換された文字列のコピーです。
出典: str.format - 4. 組み込み型 — Python 3.6.5 ドキュメント

引数の順序で指定

'{0}年{1}月{2}日'.format(2018, 8, 15)
# => 2018年8月15日

キーワードで指定

'{year}年{month}月{day}日'.format(year=2018, month=8, day=15)
# => 2018年8月15日

リスト・辞書

リスト

l = ['Mika', 'boss', 18]
'{0[0]}-{0[1]}-{0[2]}'.format(l)
# => Mika-boss-18

辞書

d = {'name': 'Aki', 'role': 'gunner', 'age': 17}
'{0[name]}-{0[role]}-{0[age]}'.format(d)
# => Aki-gunner-17

※ リストも辞書もformat()引数の位置を指定する必要がある。下記のような記述はエラーになる

l = ['Mika', 'boss', 18]
'{[0]}-{[1]}-{[2]}'.format(l)
# => エラー

位置引数・キーワード引数でリスト・辞書を展開して渡す

リスト => 位置引数*

l = ['Mika', 'boss', 18]
'{0}: {1}-{2}-{3}'.format('Keizoku', *l)
# => Keizoku: Mika-boss-18

辞書 => キーワード引数**

d = {'name': 'Aki', 'role': 'gunner', 'age': 17}
'{0}: {name}-{role}-{age}'.format('Keizoku' ,**d)
# => Keizoku: Aki-gunner-17

3. f-string (formatted string literal)

バージョン 3.6 で追加.
フォーマット済み文字列リテラル( formatted string literal )または f-string は、接頭辞 'f' または 'F' の付いた文字列リテラルです。これらの文字列には、波括弧 {} で区切られた式である置換フィールドを含めることができます。他の文字列リテラルの場合は内容が常に一定で変わることが無いのに対して、フォーマット済み文字列リテラルは実行時に式として評価されます。
出典: 2.4.3. フォーマット済み文字列リテラル

val = 'Wold'
f'Hello {val}!'
# => Hello Wold!

# リスト
l = ['Mika', 'boss', 18]
f'{l[0]}-{l[1]}-{l[2]}'
# => Mika-boss-18

# 辞書
d = {'name': 'Aki', 'role': 'gunner', 'age': 17}
f'{d["name"]}-{d["role"]}-{d["age"]}'
# => Aki-gunner-17

f'{d[name]}-{d[role]}-{d[age]}'
# => NameError: name 'name' is not defined

※ 辞書のキーは"(ダブルコーテーション)で囲わないとエラーになる。str.format()は囲わなくてもOKなのでハマりどころかもしれません。

{}内での計算

f-stringは{}内が先に計算された後に文字列展開される

val = 1.41421356
size = 4
f'res: {val:.{size}}'
# => res: 1.414

val = 2
f'Square {val*2}'
# => Square 4

{}内で関数も実行できる

def add(a, b):
  return a + b
x = 10
y = 20
f'value: {add(x, y)}'
# => value: 30

{ } を出力する

\ではなく、{{ }}の様に二重で囲むと{ }が出力できる

val = 100
f'{{}}-{val}-{{val}}-{{{val}}}-{{'
# => {}-100-{val}-{100}-{

※ 片方だけ{を出力したい場合も二重にしないとシンタックスエラーになる

書式の指定

展開する変数に:~で書式を指定して展開することが出来るようです。

num = 255
print( f'{num} to hex => {num:x}' )
# => 255 to hex => ff

d = {'year': 2018, 'month': 8, 'day': 15}
print( f'{d["year"]}-{d["month"]:02}-{d["day"]:02}' )
# => 2018-08-15

 
Pythonって書き方は1つのようなイメージがあったんのですが、文字列中の変数展開の方法も結構いろいろあるんだなと知る事ができました。
Python3からは基本的に、f-string (f'...')で良いかなという印象です。
展開する変数が配列や辞書の時は場合によってはstr.formatで位置引数(*)・キーワード引数(**)を使うほうが楽かもしれません。


[参考]

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編

Python3 辞書のループ

他の言語ではハッシュとか連想配列と呼ばれているようなキーと値が対になったデータ構造をPythonでは辞書(dictionary)と呼ぶらしいです。(厳密にはもっと違いがあるのかもしれません)

辞書をfor inでループ

dict = {'name': 'Aki', 'age': 16, 'role': 'gunner'}
for i in dict:
  print(i, end=', ')
# => name, age, role,

for inで辞書を回すとループ内ではキーが取得される
↓下記のようにdict.keys()でループさせるのと同じっぽい

dict = {'name': 'Aki', 'age': 16, 'role': 'gunner'}
for key in dict.keys():
  print(f'key:{key} value:{dict[key]}', end=', ')
# => key:name value:Aki, key:age value:16, key:role value:gunner,

辞書の値(value)でループ

dict.values()を使う

dict = {'name': 'Aki', 'age': 16, 'role': 'gunner'}
for val in dict.values():
  print(val, end=', ')
# => Aki, 16, gunner,

キー(key)と値(value)両方でループ

dict.items()を使う

for key, val in dict.items():
  print(f'key:{key} value:{val}', end=', ')
# => key:name value:Aki, key:age value:16, key:role value:gunner,

 
辞書をループ処理する時はとりあえずdict.items()を使うのが良さそうかなと思いました。


[参考]

Python3 ループいろいろ

for

s = ''
for i in range(3):
  s += str(i)
print(i)
# => 2
print(s)
# => '012'

s = ''
# 3<= i < 10 でループ
for i in range(3, 10):
  s += str(i)
print(s)
# => '3456789'

リストをループ

s = ''
arg = ['foo', 'bar', 'buz']
for i in arg:
  s += i
print(s)
# => 'foobarbuz'

リストのインデックスは取れない

リストのインデックスを取得するループ

enumerate() を使う
enumerate(iterable, start=0) - 2. 組み込み関数 — Python 3.6.5 ドキュメント

arg = ['foo', 'bar', 'buz']
for index, val in enumerate(arg):
  print( 'arg[{0}] = {1}'.format(index, val) )
# => arg[0] = foo
# => arg[1] = bar
# => arg[2] = buz

len() でリストの要素数を取ってループ

arg = ['foo', 'bar', 'buz']
for i in range( len(arg) ):
  print( 'arg[{0}] = {1}'.format(i, arg[i]) )
# => arg[0] = foo
# => arg[1] = bar
# => arg[2] = buz

enumerate()の方がクールな感じ

while

i = 0
while i < 10:
  print(i, end=', ')
  i += 1
# => 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

breakとcontinue

break - ループから抜ける

i = 10
while i > 0:
  if i == 3:
    break
  print(i, end=", ")
  i -= 1
# => 10, 9, 8, 7, 6, 5, 4,

continue - 処理をスキップ

for i in range(10):
  if i == 3 or i == 7:
    continue
  print(i, end=', ')
# => 0, 1, 2, 4, 5, 6, 8, 9,

複数のリストを同時にループで回す

zip

zip(*iterables)
それぞれのイテラブルから要素を集めたイテレータを作ります。

この関数はタプルのイテレータを返し、その i 番目のタプルは引数シーケンスまたはイテラブルそれぞれの i 番目の要素を含みます。このイテレータは、入力イテラブルの中で最短のものが尽きたときに止まります。
出典: zip(*iterables) - 2. 組み込み関数 — Python 3.6.5 ドキュメント

リストの要素数が異なる時は最も要素が少ないリスト長でのループになる

names = ['Mika', 'Aki', 'Mikko']
ages = [18, 17, 16]
for name, age in zip(names, ages):
  print(name, age)
# => Mika 18
# => Aki 17
# => Mikko 16

roles = ['boss', 'gunner', 'driver', 'correspondent']
for name, role, age in zip(names, roles, ages):
  print(name, role, age)
# => Mika boss 18
# => Aki gunner 17
# => Mikko driver 16

zip_longest

tertools.zip_longest(*iterables, fillvalue=None)
各 iterable の要素をまとめるイテレータを作成します。iterable の長さが違う場合、足りない値は fillvalue で埋められます。最も長い itarable が尽きるまでイテレーションします。
出典: itertools.zip_longest(*iterables, fillvalue=None) - 10.1. itertools — 効率的なループ実行のためのイテレータ生成関数 — Python 3.6.5 ドキュメント

異なるリストの要素数が異なる時、足りない要素を任意の値で埋めて最も長い要素数でループすることができる

from itertools import zip_longest

names = ['Mika', 'Aki', 'Mikko']
ages = [18, 17, 16]
roles = ['boss', 'gunner', 'driver', 'correspondent']

for name, role, age in zip_longest(names, roles, ages, fillvalue = '---'):
  print(name, role, age)
# => Mika boss 18
# => Aki gunner 17
# => Mikko driver 16
# => --- correspondent ---

fillvalueで埋める値を指定しない場合はNoneが出力される

from itertools import zip_longest が無くてもPython v3.6.5だとエラーにならなかったので、このimportが必要なのかどうか不明

ループ - else

ループにelse節を続けるとelse節の中がループの終了時に実行される

for i in range(10):
  if i == 3 or i == 7:
    continue
  print(i, end=', ')
else:
  print("loop end!")
# => 0, 1, 2, 4, 5, 6, 8, 9, loop end!

↓ 下記と実質的に同じ気が....

for i in range(10):
  if i == 3 or i == 7:
    continue
  print(i, end=', ')
print("loop end!")
# => 0, 1, 2, 4, 5, 6, 8, 9, loop end!

ループ後のelseの使いみちがイマイチ謎ぃ

ハッシュ?辞書?のループはまた今度。


[参考]