2021.10.28
2022.02.02
Fabeee社員ブログ
こんにちは!FabeeeのPythonエンジニア、凛子です。
今回のトピックは「Pythonでの繰り返し処理」です。
プログラミングをしていると同じ処理を繰り返したい場面が多々ありますよね。
そんな時に使える繰り返し処理の方法についてご紹介していきます。
目次
【基本編】
配列の全要素を繰り返したい:for
同じ処理を繰り返したい時にはfor文を使います。
最も基本的な使い方はこちら。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
# for 配列の中身 in 配列:
for sushi in sushi_list:
print(sushi)
>>> salmon
>>> tuna
>>> shrimp
>>> negitoro
for文では「配列の中身(要素)がひとつずつ取り出してそれについて処理をする」というのを配列の中身全てに対して行います。
今何回目の繰り返しか知りたい:enumerate()
繰り返し処理をしていて今何回目かを知るにはenumerate()関数が有用です。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
# for カウンタ, 配列の中身 in enumerate(配列):
for i, sushi in enumerate(sushi_list):
print(i, sushi)
>>> 0 salmon
>>> 1 tuna
>>> 2 shrimp
>>> 3 negitoro
# 上記の例だとカウンタは0から始まる
# 1などから始めたい場合はenumerate()関数にstart引数を指定
for i, sushi in enumerate(sushi_list, 1):
print(i, sushi)
>>> 1 salmon
>>> 2 tuna
>>> 3 shrimp
>>> 4 negitoro
for i, sushi in enumerate(sushi_list, 3):
print(i, sushi)
>>> 3 salmon
>>> 4 tuna
>>> 5 shrimp
>>> 6 negitoro
ちなみにカウンタの変数名は他の多くの言語同様、慣例的に i を使うことが多いです。
勿論 i 以外の変数名を使用することもできますが、特段理由がない場合は i を使うのが無難です。
指定回数だけ繰り返したい:range()
配列に関係なく「2回だけ繰り返したい」というような時もありますよね。
そんな時はrange()関数を使うことで実現できます。
# range()の引数に繰り返したい回数を指定
for i in range(2):
print(i)
>>> 0
>>> 1
# 配列と組み合わせて使う
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
for i in range(2):
print(sushi_list[i])
>>> salmon
>>> tuna
range()関数についてはこちらの記事でも詳しく解説していますのでぜひご覧ください。
Pythonのrange関数について
繰り返しを中断させたい:break
とある条件の時に繰り返しのforループを抜けたいという時には、breakを使います。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
for sushi in sushi_list:
if sushi == 'shrimp':
break # ループを抜ける
else:
print(sushi)
>>> salmon
>>> tuna
shrimpだった場合に処理をせずforループを抜けているため、shrimpより前にあるsalmonとtunaのみが出力されています。
繰り返しをskipしたい:continue
逆にとある条件の時のみ処理を行いたい/行いたくないという時にはcontinueを使います。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
for sushi in sushi_list:
if sushi == 'shrimp':
continue # ループをskip
else:
print(sushi)
>>> salmon
>>> tuna
>>> negitoro
shrimpの時だけ繰り返しがスキップされ、処理されていないことがわかります。
【応用編】
速度を求める:リスト内包表記
for文は1文でも書くことができ、これをリスト内容表記といいます。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
# [繰り返したい処理 for 繰り返し中で使う変数名 in 配列]
upper_list = [sushi.upper() for sushi in sushi_list ]
print(upper_list)
>>> ['SALMON', 'TUNA', 'SHRIMP', 'NEGITORO']
# 型はリスト
print(type(upper_list))
>>> <class 'list'>
上記の例では「配列の中身を全て大文字にする」という処理を行い新たな配列を作っています。
また、あまりやりすぎると可読性が下がるのでは?という論争もありますが
ifなどと組み合わせて使うこともできます。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
# [繰り返したい処理 for 繰り返し中で使う変数名 in 配列 if 条件]
[sushi.upper() for sushi in sushi_list if sushi != 'shrimp' ]
>>> ['SALMON', 'TUNA', 'NEGITORO']
「shrimpでない場合」という条件をifで追加して繰り返し処理を行なっています。
(先ほどからshrimpばかり槍玉に上がっていますが筆者はエビが一番好きです)
さてこのリスト内包表記、何がよいかというと速いのです!
for文を回すよりは速度が出るため、少しの間も惜しいような速度が求められるプログラム中では使っていくとよいでしょう。
ちなみにとある処理を筆者の環境で試したところ、下記のようになりました。
for文の平均 | 2.151秒 |
---|---|
リスト内包表記の平均 | 1.621秒 |
ここではあまり複雑な計算をしていないので大きな違いは出ませんが、重い処理を行う場合には大きな差が出てきます。
計測に使用したコード
## 「適当な計算処理を100万回繰り返したリストを生成」し速度をそれぞれ3回ずつ計測
import time
def test_for():
""" for文ver """
start = time.time()
results = list()
for i in range(10000000):
results.append(i / 2 * 3 + 4 - 5)
end = time.time()
elapsed_time = end - start
print(f'forの処理時間(秒) {elapsed_time}')
return elapsed_time
def test_list_comprehension():
""" リスト内包表記ver """
start = time.time()
results = [ (i / 2 * 3 + 4 - 5) for i in range(10000000) ]
end = time.time()
elapsed_time = end - start
print(f'リスト内包表記の処理時間(秒) {elapsed_time}')
return elapsed_time
for_result1 = test_for()
for_result2 = test_for()
for_result3 = test_for()
list_result1 = test_list_comprehension()
list_result2 = test_list_comprehension()
list_result3 = test_list_comprehension()
for_average = (for_result1 + for_result2 + for_result3) / 3
list_average = (list_result1 + list_result2 + list_result3 ) / 3
print(f'for文の平均 {for_average}')
print(f'リスト内包表記の平均 {list_average}')
二つの配列をがっちゃんこしてループ:zip()
zip()という関数を使えば、ふたつの配列をがっちゃんこしてループすることもできます。
plate_list = ['sushi', 'pasta', 'escargot', 'hamburger']
country_list = ['Japan', 'Italy', 'France', 'USA']
# for 配列Aの要素, 配列Bの要素 in zip(配列A, 配列B):
for plate, country in zip(plate_list, country_list):
print(plate, country)
>>> sushi Japan
>>> pasta Italy
>>> escargot France
>>> hamburger USA
配列Aのひとつめと配列Bのひとつめ、配列Aのふたつめと配列Bのふたつめ、、、というペアで繰り返し処理ができます。
ふたつの配列をこのように同時に回したい時は意外とあるので、そういった時に重宝します。
繰り返し処理の話から少しそれますが、以下zip()関数のTipsです。
# 3つ以上の配列でもzip()でがっちゃんこループができる
plate_list = ['sushi', 'pasta', 'escargot', 'hamburger']
country_list = ['Japan', 'Italy', 'France', 'USA']
cook_list = ['Yamada', 'Matteo', 'Cedric', 'John']
for plate, country, cook in zip(plate_list, country_list, cook_list):
print(plate, country, cook)
>>> sushi Japan Yamada
>>> pasta Italy Matteo
>>> escargot France Cedric
>>> hamburger USA John
# 要素数が異なる場合、多い方が無視される
plate_list = ['sushi', 'pasta', 'escargot', 'hamburger']
country_list = ['Japan']
for plate, country in zip(plate_list, country_list):
print(plate, country)
>>> sushi Japan
# 要素数が異なる配列について、多い方を無視せず適当な値で埋めたい場合
from itertools import zip_longest
for plate, country in zip_longest(plate_list, country_list, fillvalue='Somewhere'):
print(plate, country)
>>> sushi Japan
>>> pasta Somewhere # zip_longest()関数のfillvalue引数の値で埋められる
>>> escargot Somewhere
>>> hamburger Somewhere
# fillvalue引数は省略可能、指定しなかった場合Noneで埋められる
for plate, country in zip_longest(plate_list, country_list):
print(plate, country)
>>> sushi Japan
>>> pasta None # デフォルトではNone
>>> escargot None
>>> hamburger None
最後に必ず処理:else
for文の最後に必ず(※1)行いたい処理をPythonではfor-elseで書くことができます。
for文にelseが書けない言語も多いのでPython特有の書き方ですね。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
for sushi in sushi_list:
print(sushi)
else:
print('Finished!!') # for文の最後に一度だけ行われる処理
>>> salmon
>>> tuna
>>> shrimp
>>> negitoro
>>> Finished!!
# 配列が空で、for文が一度も回らなかった際にもelseは実行される
for hoge in list():
print(hoge)
else:
print('Finished!!')
>>> Finished!!
※1 ただしbreakでforを抜けた場合にはelseは実行されないため要注意です。
sushi_list = ['salmon', 'tuna', 'shrimp', 'negitoro']
for sushi in sushi_list:
print(sushi)
if sushi == 'shrimp':
break
else:
print('Finished!!')
>>> salmon
>>> tuna
>>> shrimp
おわりに
いかがでしたでしょうか。
単純な繰り返しから、ひとひねりした繰り返しまでご紹介してきました。
この記事がみなさまのプログラミングの参考になれば幸いです!