"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

PythonのForループのスマートな書き方のまとめ(1)

今回は、Pythonのforループの書き方を整理しとこうかと思います。

頻繁に使うのですが、しばらく離れると、ふと迷う。

そういう意味では、スライスと同じですから。

f:id:arakan_no_boku:20181124131221j:plain

 

変数をカウントアップ・ダウンするループ

 

変数をカウントアップ・ダウンする形から

 

整数のカウントアップ・ダウン

 

C言語だと「for i=0;i<10;i++{}」でループを回すようなパターンです。

# 0-9
for i in range(10):
    print(i)

# 9-0
for i in range(9,-1,-1):
    print(i)

# 1-10
for i in range(1,11):
    print(i)

基本は「range(start,end,step)」です。

startは省略されると「0」、stepは省略されると「1」です。

endは指定された数字のひとつ手前まで繰り返します。

だから、endが10だと、iには9まではいりますし、逆に減っていくパターンだと、0まで繰り返したい場合はendは「-1」にしないといけないわけです。

ちなみに。

上記の「range()」は、整数専用です。

浮動小数点数は使えません。

 

少数のカウントアップ・ダウン

 

少数の1.0から3.0まで0.1きざみでループさせたい時は、「range()」ではなく、「numpy.arange()」を使う必要があります。

import numpy as np

for n in np.arange(1.0,3.0,0.1):
    print(n)

これで、1.0から2.9までの繰り返しになります。 

 

ひとつのlistだけループで処理する

 

listを処理する場合は、値だけ取得する方法と、値と添え字をセットで取得する方法があります。

 

値だけが欲しい場合。

 

a = ['a','b','c','d','e','f','g']
    
for x in a:
    print(x)

inだけです。

'a'、'b'などの値が順に変数(x)にはいってくる感じです。

今度は値だけでなく、添え字も一緒に欲しい場合。

a = ['a','b','c','d','e','f','g']
    
for i,name in enumerate(a):
    print(i,":",name)

 

添え字と値をセットで取得する方法

 

enumerate()を使うと、添え字と値の両方を取得できます。 

上記の例だと、「i」に0,1,2・・の添え字、「name」に'a','b','c'・・の値が順番にセットされます。

結果出力はこんな感じです。

0 : a
1 : b
2 : c
3 : d
4 : e
5 : f
6 : g

range(len(a))よりスマートなので、自分はこちらを愛用してます。

 

複数のリストがあり深くネストするループ処理

 

総当たりで処理しないといけない場合になります。

 

普通にネストさせる場合

 

例えば、以下のように4つのリストがあるケースを考えます。

a = ['a','b','c']

b = ['5','6','7']

c = ['d','e','f']

d = ['1','2','3']

それらを総当たりで組み合わせて処理しないといけないような場合、普通に考えたり、こんな感じにループを重ねます。

a = ['a','b','c']
b = ['5','6','7']
c = ['d','e','f']
d = ['1','2','3']

for w in a:
    for x in b:
        for y in c:
            for z in d:
                print(w,x,y,z)

 

itertoolsを使ってスマートに書く場合

 

でも、itertoolsを使うと、上記と同じことが、以下のように、かなりスマートにかけます。

import itertools as itr
a = ['a','b','c']
b = ['5','6','7']
c = ['d','e','f']
d = ['1','2','3']

for w,x,y,z in itr.product(a,b,c,d):
    print(w,x,y,z)

これは、for文の深さが深くなればなるほど差がでますので、自分はもっぱら後者にしてます。

 

 

複数のlistを連結してループする

 

例えば、以下のような2つのlistを考えます。

a = ['01','02','03']
b = ['04','05','06']

この2つを連結して、['01','02','03','04','05','06']のlistとしてループを回したい場合です。

 

itertoolsのchainを使って、スマートに書く場合

 

以下のようになります。

import itertools as itr

a = ['01','02','03']
b = ['04','05','06']

for x in itr.chain(a,b):
    print(x)

itertoolsのchainは単純に連結してくれるので、あたかも、['01','02','03','04','05','06']のように処理されます。

 

 

複数のリストを並列に扱ってループする

 

a[0]とb[0]、a[1]とb[1]みたいに、2つのリストを並列に扱いたい場合もあります。

というか、この方が多いです。

キーのリスト、バリューのリストを用意して、組合せて辞書型に初期化するとか・・。

 

並列の普通のやり方

 

普通のやり方だと、こんな感じです。

a = ['01','02','03']
b = ['04','05','06']

for i in range(len(a)):
    print(a[i],":",b[i])

 

zip()を使った例

 

同じことをzip() を使って以下のようにも書けます。

a = ['01','02','03']
b = ['04','05','06']

for x,y in zip(a,b):
    print(x,":",y)

短さは同じですけど、zip()の方が若干スマートではあります。

ですが、zip()という名前と処理の連想がしづらいという人もいるので、まあ、どっちもどっちですかね。

ちなみに、zip()は2つのlistの長さが違う場合は、短い方の長さだけ処理します。

 

zip()で長い方のリストにあわせて処理する場合

 

長い方に合わせたい場合は、「zip_longest」を使います。

その例です。

import itertools as itr

c = ['a0','b0','c0','d0']
d = ['04','05','06']

for x,y in zip(c,d):
    print(x,":",y)

for x,y in itr.zip_longest(c,d):
    print(x,":",y)   

上のzip()=短い方にあわせるの結果は、こうです。

a0 : 04
b0 : 05
c0 : 06 

下の方(zip_longest)の結果はこうです。

a0 : 04
b0 : 05
c0 : 06
d0 : None

 まあ・・。

正直、zip_longest()を実際に使うケースには、まだお目にかかったことはないです。

 

Python以外で見たことがない特殊な使い方

 

これはスマートなやり方ではないですが、情報だけ。

Pythonってforループに対して、elseが使えます。

この「else」は、「ループでbreak文が実行されなければ処理を実行し、そうでなければ実行しない」という妙な動きをします。

その2つのケースの例です。

a = [1,2,3,4,5]

for i in a:
    print("Loop",i)
    if(i > 4):
          break
else:
    print("break文は実行されなかった")

for i in a:
    print("Loop",i)
else:
    print("break文は実行されなかった")

実行結果です。

上の方は「break」しているので、こうなります。

Loop 1
Loop 2
Loop 3
Loop 4
Loop 5 

下の方は「break」文が実行されてないので、elseブロック内の処理も動いてます。

Loop 1
Loop 2
Loop 3
Loop 4
Loop 5
break文は実行されなかった

今のところ、この機能の使いどころは、自分にもよくわかりません。

ですが。

まあ、引き出しは多い方がいいかな・・ということで、一応書いてはおきますが。

ではでは。

arakan-pgm-ai.hatenablog.com