↓もし、良かったらSNSでの紹介よろしくお願いします。

【Python】for文、基本から実践的なテクニック

bootstrap

本記事では、Pythonでの繰り返し処理を紹介していきます。
初心者から中級者まで満足できるfor文の記事となっています。

ちなみに、
「Pythonのは遅い」と嘆いてる方は、
for文を疑ってみると良いかもしれません。

Pythonでの基本的な書き方

Pythonでfor文を利用して、hogeを10回表示してみます。

for i in range(10):
    print('hoge')

実行結果はつぎのようになります。

hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge

オーソドックスなfor文はない

ちなみに、多言語に親しんだ人はinとlistを使った
for文には違和感があると思います。

けれど、Pythonには、オーソドックスなfor文はありません。
なので、リストとinを使った方法で書きましょう。

1からfor文を回す

上記の例では、iは0はじまりで、iを表示すると0~9が表示されます。
しかし、1から10を表示したい場合があります。
これは、つぎのようにrangeを使うことで実現できます。

for i in range(1,11):
    print(i)

このコードを見て誤植かと思う人もいると思います。
11では無く、「10なんじゃないの?」

しかし表示されるのは、

1
2
3
4
5
6
7
8
9
10

です。

Pythonはrange(1,11)を1~10の数として読み込みます。
つぎのように最初の値は設定値ですが、最後の値は1つ引いた値になっています。
間違いやすいので注意しましょう。

range(最初の数,最後の数-1)

1個飛ばしのfor文

1個とばしのfor文を書くときは次のように書きます。

for i in range(1,10,2):
    print(i)

表示結果は

1
3
5
7
9

です。ちなみに偶数にのみ表示したい場合は

for i in range(0,10,2):
    print(i)

と書くと良いです。

この書き方は一般化すると

range(最初の数,最後の数-1,ステップ数)

とかくことができます。

ステップ数とは次の数に足す数のことで、
デフォルト値は1となっているので数が連続で表示されます。

2だと1の次は2を足した3になるので、1つ飛ばしになります。

for文を逆から回す

rangeを逆に回す場合は、次のように書きます。

for i in range(1,10)[::-1]:
    print(i)

これは、listのスライス機能を利用した書き方です。
スライス機能とは、リストの添え字でリストを部分的に取り出せる方法です。

>>> hoge_list = [1,2,3,4,5,6]
>>> hoge_list[1:3]
[2, 3]
>>> hoge_list[1:5:1]
[2, 3, 4, 5]
>>> hoge_list[::-1]
[6, 5, 4, 3, 2, 1]

用途に応じた書き方

基本的なfor文について説明しましたが、
次は、細かい用途に応じたfor文を紹介します。
これを使いこなすことができれば、脱初心者でしょう。

また、while文との使い分けに関しては次の記事で参考にしてください。

https://www.tech-tech.xyz/while/

2つのリストを組み合わせるzip

たとえば、テストの点数と氏名のリストがあるとしましょう。
氏名と点数の一覧を表示するプログラミングを書く場合どうしましょう?
一例として次のような書き方があります。

names = ['花子','太郎','さとし']
scores = [90,50,40]
for i in range(len(names)):
  print('%sさんの点数は%s点です。'%(names[i],scores[i]))

しかし、このようなfor文はzipを利用することで簡潔にかけます。
ちなみに、圧縮ファイルzipとは関係ないです。
使い方は、つぎのようになります。

names = ['花子','太郎','さとし']
scores = [90,50,40]
for name,score in zip(names,scores):
  print('%sさんの点数は%s点です。'%(name,score))

このコードの表示は、

花子さんの点数は90です。
太郎さんの点数は50です。
さとしさんの点数は40です。

となります。

リストの要素と何番目かを利用する場合はenumerate

リストの要素と、それが何番目かを表示するとき
rangeを使って次のように書くことができます。

names = ['花子','太郎','さとし']
for i in range(len(names)):
  print('%sさんは%s番目です。'%(names[i],i))

しかし、このような時にはenumrateを使ったほうが簡潔に書けます。

names = ['花子','太郎','さとし']
for i,name in enumerate(names):
  print('%sさんは%s番目です。'%(name,i))
花子さんのは0番目です。
太郎さんのは1番目です。
さとしさんのは2番目です。

0番目というのは変なので、1番目から表示してみます。

enumerate(リスト,最初の番号)

とすることで、スタートの番号を選択することができます。

names = ['花子','太郎','さとし']
for i,name in enumerate(names,1):
  print('%sさんは%s番目です。'%(name,i))

実行結果

花子さんのは1番目です。
太郎さんのは2番目です。
さとしさんのは3番目です。

breakでループを抜ける

途中でfor文を止めたいときは、breakを使うことでfor文のループから抜けられます。

>>> for i in range(10):
...     if i == 3:
...         break
...     print(i)
...
0
1
2

continueで処理を飛ばす

処理を飛ばしたいときはcontinueを使います。

>>> for i in range(10):
...     if i <= 3:
...         continue
...     print(i)
...
4
5
6
7
8
9

itertoolsで2重のfor文が簡潔に

itertools.productを利用すると2重のfor文を簡潔に書くことができます。

itertools.productで九九を計算する

ためしに、九九を計算するプログラムをitertools.productで書いてみます。
普通だと、2重でfor文を書くことになりますが次のように簡潔にかけます。

import itertools
nums = range(1,10)
for i,j in itertools.product(nums, nums):
    print("%s * %s = %s" % (i, j, i*j))

実行結果

1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
...
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
...
9 * 9 = 81

微妙なfor文

for文で意見がわかれるところについてまとめてみます。

Python2でのrange

Python2では、ループを指定の回数、
回すだけならrangeよりもxrangeを使うべきです。

なぜかというと、
Python2のrangeはリストオブジェクトを返します。
そのため、リストを生成するためにメモリを確保するので非効率です。
なので、リストの代わりにイテレータを返すxrangeを利用するほうが良いです。

for i in xrange(1,11):
    print(i)

for文のelse文

Pythonでは、for文とelseを組み合わせる文法があります。
elseの中の処理はfor文の全ての処理が実行された場合に実行されます。

for i in range(10):
    print(i)
else:
    print('complete')

実行結果

0
1
2
3
4
5
6
7
8
9
complete

for文がbreak文で打ち切られた場合、
elseブロックの処理は実行されません。

for i in range(10):
    print(i)
    if i == 2:
      break
else:
    print('complete')

実行結果

0
1
2

挙動がわかり辛いため、あまり使わないほうが良いと思われます。
if文のelseブロックはif内の処理が実行されたないとき実行されるのにたいして、
for文のelseブロックはfor内の処理が実行されたときに実行されるため混乱を招くことがあります。

このような処理を書きたい場合、is_completeのようなフラグを用意して、
breakする前にis_complete=Falseなどとする実装のほうがわかりやすいです。

is_complete = True
for i in range(10):
    print(i)
    if i == 2:
        is_complete = False
        break
if is_complete:
    print('complete')

range(len(list))は書きなおせる

range(len(list))のような形式でfor文を回す場合、
enumerateまたはzipを利用することでエレガントに書けることが多いです。
range(len(list))を見つけたら見直してみると良いでしょう。

中級者への一歩map,filter

Pythonでは繰り返し処理で新たなリストを生成する場合は
for文よりもmapやfilterを利用したほうが良いばあいが多いです。

たとえば、偶数のリストをmapで作成しすると次のようになります。

>>> mapfunc =lambda x: 2*x 
>>> even_numbers = list(map(mapfunc,range(10)))
>>> print(even_numbers)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

奇数を取り除くという観点で偶数のリストを作ることもできます。
そのときは、filterを使います。

>>> filterfunc = lambda x: x%2 == 0
>>> even_numbers = list(filter(filterfunc,range(10)))
>>> print(even_numbers)
[0, 2, 4, 6, 8]

map,filterのまとめ

  • ある値を関数で置き換える場合はmap
list(map(関数,値のリスト))
  • ある値を取り除く場合はfilter
list(filter(真偽値を返す関数,値のリスト))

listに変換しているのは、
Python3からmapやfilterはイテレーターを返すようになったからです。

意図しないイテレーターはバグの原因になるので、
基本的には、list化する習慣をつけると良いと思います。

内包表記も便利

listやdictを繰り返しで新たに生成する場合は、
内包表記がスマートな場合が多いです。

>>> [2 * v for v in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> {k:2*k for k in range(10)}
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

Pythonが遅いのはfor文が原因!?

単純な繰り返し処理をPythonでおこなうべきではありません。
というのも、numpyで可能な繰り返し処理はなるべくnumpyでやるべきだからです。

numpyとは?

numpyとはPythonの計算ライブラリです。
C言語で書かれているため処理が高速でリストを柔軟に扱うことができます。
インストールは簡単でpipで入れることができます。

pip install numpy

玄人はnumpyを使い倒す

>>> import numpy as np
>>> a = np.array([0,1,2,3,4,5,6,7,8,9])
>>> a = 2 * a
>>> print(a)
[ 0  2  4  6  8 10 12 14 16 18]
>>> a = a - 10
>>> print(a)
[-10  -8  -6  -4  -2   0   2   4   6   8]
>>> a = np.abs(a)// 絶対値
>>> print(a)
[10  8  6  4  2  0  2  4  6  8]
>>> a = np.power(a,3)// 3乗する
>>> print(a)
[1000  512  216   64    8    0    8   64  216  512]
>>> a.mean()// 平均
260.0
>>> a.std()// 標準偏差
308.2077221615318
>>> a.var()// 分散
94992.0

for文の完了率を表示する

for文の中の処理が重い場合、処理の進捗率が気になることがあります。
そんなときは、tqdmが便利です。

for文がどこまで完了したのかのプログレスバーを表示してくれます。
pipでインストールできます。

pip install tqdm

使い方

tqdmで囲むだけで簡単に使えます。

from tqdm import tqdm
import time
for i in tqdm(range(100)):
    time.sleep(0.1)