書いたプログラムが止まらなくなって、あわてて画面ごと閉じてしまった。あるいは、何回くり返せばいいのか決められず、for文を書きかけたまま手が止まった。
Pythonを学び始めた方から、どちらもよく聞く話です。
その2つは、実は同じ道具でつながっています。回数が決まっていない繰り返しを書くための構文、while文です。
この記事では、while文の基本から、無限ループとの付き合い方、実務で本当に役立つ使いどころまでを順番に整理していきます。
while文は、条件が成り立つ間だけくり返す¶
まずは仕組みから確認しましょう。while文は、条件式が真である限り、同じ処理を何度でもくり返します。
公式ドキュメントの言い方を借りると、式をくり返し評価し、真であれば中の処理を実行する構文です。偽になった時点でループを抜けます。
日本語にすると、〜の間ずっと、という意味になります。whileという英単語が、そのまま動作を表しているわけです。
書き方はとてもシンプルです。whileのうしろに条件式を置き、コロンで閉じて、くり返したい処理をインデントで下にぶら下げます。
言葉だけでは伝わりにくいので、いちばん小さな例を動かしてみましょう。
count = 1
while count <= 3:
print(f"{count}回目")
count += 1
print("おわり")
実行すると、1回目から3回目までが表示されて終わります。countが4になった瞬間に条件が偽になり、ループを抜けるからです。
ここで見落とせないのが、最後の count += 1 という行です。これがないとcountはずっと1のままで、条件が永遠に真になってしまいます。
forとwhile、どちらを使えばいいのか¶
初心者がいちばん迷うのは、たぶんここでしょう。どちらも繰り返しなのに、なぜ2種類あるのか分かりにくいのです。
判断の基準はひとつだけです。繰り返す回数が最初から分かっているならfor、分からないならwhile。 これでほとんどの場面は決まります。
具体的な場面に落としてみると、境目がはっきりします。
| やりたいこと | 使うもの |
|---|---|
| リストの中身を全部処理する | for |
| 決まった10回だけ試す | for |
| 正しい入力が来るまで聞き直す | while |
| ファイルの終わりまで読み続ける | while |
| 通信が成功するまでやり直す | while |
回数が決まっている繰り返しなら、for文とrange関数の組み合わせのほうが読みやすくなります。使い方はこちらでまとめています。【関連記事】pythonのrange関数の使い方は?ステップ指定と逆順ループの小技をご紹介!
条件に書けるのは、比較式だけではない¶
whileのうしろに置けるのは、大小の比較だけではありません。Pythonは、あらゆる値を真か偽のどちらかとして扱えます。
たとえば空のリストは偽で、中身のあるリストは真です。0は偽で、0以外の数値は真になります。
この性質を知っていると、条件をぐっと短く書けるようになります。
tasks = ["資料作成", "レビュー", "提出"]
while tasks:
task = tasks.pop(0)
print(f"処理中: {task}")
print("タスクが空になりました")
while tasks: は、リストに中身がある間ずっと、という意味になります。len()を使って0と比べなくても済むので、慣れるとこちらのほうが自然に読めます。
ただし、Noneと空文字と0がすべて偽になる点は事故のもとでもあります。判定の細かい話は別の記事にまとめました。【関連記事】Pythonの真偽値を深く知る!if文で意外とハマるNoneや空リストの挙動を解説
無限ループは、失敗ではなく道具¶
while True: と書くと、条件が永遠に真のままになります。いわゆる無限ループです。
学び始めた頃は、書いてはいけないものに見えるかもしれません。ですが実務では、意図して使う場面のほうがずっと多いくらいです。
代表的なのが、正しい入力が来るまで何度でも聞き直す処理です。
while True:
answer = input("好きな数字を入れてください: ")
if answer.isdigit():
print(f"{answer} ですね")
break
print("数字で入力してください")
条件そのものは常に真ですが、途中のbreakが脱出口になっています。無限ループが危険なのは、抜ける道を用意し忘れたときだけです。
止まらなくなったときの止め方¶
それでも、抜けられないループを書いてしまうことはあります。落ち着いて対処すれば、何も壊れません。
ターミナルで動かしているなら、キーボードでCtrl+Cを押してください。実行中のプログラムに割り込みがかかり、その場で止まります。
Jupyter NotebookやGoogle Colabなら、セルの停止ボタンを押します。エディタで実行しているときも、実行パネルに同じ役割のボタンがあります。
私は10年ほどエンジニアとして開発に関わってきましたが、無限ループを一度も書かずに終わった年はありません。止め方さえ知っていれば、書き直すだけで済む話です。
breakとcontinueで、流れを細かく操る¶
ループの途中で流れを変える方法は2つあります。名前が似ているので、違いをはっきりさせておきましょう。
breakはループそのものを終わらせます。continueは今回の周回だけを切り上げて、次の周回へ進みます。
公式ドキュメントでは、continueは残りの処理を飛ばして条件の判定に戻る、と説明されています。
動きを見比べると、違いがすぐに分かります。
number = 0
while number < 10:
number += 1
if number % 2 == 0:
continue # 偶数はスキップして次の周回へ
if number > 7:
break # 7を超えたらループごと終了
print(number)
表示されるのは1、3、5、7の4つです。偶数はcontinueで飛ばされ、9に届く前にbreakで抜けています。
ひとつ注意したい点があります。continueを使うとき、カウンタを増やす行はcontinueより前に書かなければいけません。
うしろに書くと、continueへ入った瞬間からカウンタが増えなくなり、そのまま無限ループになります。私も何度かやりました。
breakが抜けるのは、いちばん内側のループだけ¶
ループの中にループを書くこともできます。このとき勘違いしやすいのが、breakの効く範囲です。
breakが終わらせるのは、自分がいる、いちばん内側のループだけです。外側のループはそのまま続きます。
実際に動かすと、思っていた動作と違うことに気づけます。
row = 0
while row < 3:
column = 0
while column < 3:
if column == 1:
break # 内側のループだけ抜ける
print(f"row={row}, column={column}")
column += 1
row += 1 # 外側は3回とも実行される
外側まで一気に抜けたいときは、フラグ用の変数を用意するか、処理ごと関数に切り出してreturnで戻すのが定番です。関数にしてreturnで抜けるほうが、たいていの場合は読みやすくなります。
while ... else という、あまり知られていない書き方¶
whileにはelseを付けられます。elseはif文でしか見ない、と思っていた方は驚くところでしょう。
動きは少し独特です。ループが条件で自然に終わったときだけelseが動き、breakで抜けたときは動きません。
言葉で読むより、表で見比べたほうが早いはずです。
| ループの終わり方 | elseは動くか |
|---|---|
| 条件が偽になって終わった | 動く |
| breakで抜けた | 動かない |
| continueを使った | 最後まで回りきれば動く |
探しものが見つからなかったときの処理を書くのに向いています。for文にも同じelseがあり、考え方は共通です。【関連記事】for-else 文とは?else は if だけじゃない、ニッチで便利な使い道
セイウチ演算子で、whileの条件がすっきりする¶
Python 3.8から使えるようになった := を組み合わせると、whileの書き方が短くなります。読み方はセイウチ演算子です。
代入と判定を1行にまとめられるのが利点で、提案書であるPEP 572にも、while条件の中で使う例が載っています。
入力を受け取り続ける処理で比べると、違いがよく分かります。
# セイウチ演算子なし
line = input("入力(空行で終了): ")
while line != "":
print(f"受け取りました: {line}")
line = input("入力(空行で終了): ")
# セイウチ演算子あり
while (line := input("入力(空行で終了): ")) != "":
print(f"受け取りました: {line}")
同じ行を2回書かずに済んでいるのが分かるでしょうか。書き忘れによるバグも自然と減ります。
便利な一方で、条件式が長くなるときは無理に使わないほうが読みやすくなります。 使いどころの判断はこちらで詳しく扱っています。【関連記事】Pythonのセイウチ演算子(:=)とは?代入式で何が変わる?
実務でwhileが活きるのは、リトライ処理¶
学習中は、入力の受け取りくらいしか出番がないかもしれません。ですが仕事で書くコードでは、通信のやり直しにwhileがよく登場します。
ネットワークは失敗するものです。一度エラーになっても、少し待って試し直せば通ることがよくあります。
試す回数に上限を決めて、成功したら抜ける。この形が定番です。
import time
max_retry = 3
attempt = 0
while attempt < max_retry:
attempt += 1
try:
result = fetch_data() # 自分で用意した通信処理
print("成功しました")
break
except Exception as error:
print(f"{attempt}回目は失敗: {error}")
time.sleep(2 ** attempt) # 2秒、4秒、8秒と待つ
else:
print("上限まで試しましたが失敗しました")
待ち時間を回ごとに伸ばすやり方を、指数バックオフと呼びます。相手のサーバーへ負荷をかけないための、基本的な作法です。
ここでもwhile ... elseが効いています。成功してbreakで抜けたときは、最後のメッセージが出ません。
sleepの使い方や待ち時間の考え方は、こちらで詳しく扱っています。【関連記事】Pythonのtimeモジュールとは?sleepで待つ・処理時間を測る基本を初心者向けに解説
初心者がつまずきやすいところ¶
最後に、実際によく見かける失敗を並べておきます。心当たりがないか確認してみてください。
| 症状 | 原因 | 対処 |
|---|---|---|
| プログラムが止まらない | 条件を変える処理がない | カウンタを増やす行を入れる |
| 1回も実行されない | 最初から条件が偽になっている | 変数の初期値を見直す |
| continueを入れたら固まった | カウンタの更新がcontinueより後 | 更新を前へ移す |
| IndentationErrorが出る | 中の処理をインデントしていない | 半角スペース4つ分下げる |
| 最後の1回だけ足りない | 条件が < と <= で食い違っている | 境界の値を紙に書いて確かめる |
圧倒的に多いのは1つめです。条件に使っている変数が、ループの中で必ず変わるかどうか。 ここだけ確認する癖をつけると、事故はほとんど防げます。
まとめと、次の一歩¶
覚えることは、そう多くありません。回数が決まっていない繰り返しはwhile、決まっているならfor。まずはこの使い分けから始めてください。
無限ループも怖いものではなく、breakという脱出口をセットで用意すれば、頼れる道具に変わります。
手を動かすなら、数当てゲームがちょうどいい題材です。正解するまで入力を受け取り続ける処理を書くと、この記事の内容がひととおり登場します。
ここまでお読みいただきありがとうございました。