Pythonで「何回くり返すか」を正確に指定してループを書くとき、中心になるのがrangeとfor文です。
本記事では、基本から実務でそのまま使えるパターンまで、20の具体例を通して詳しく解説します。
図解やコード例を交えながら、単なる文法説明だけでなく、現場で役立つ書き方や落とし穴の回避方法まで網羅的に学べる構成にしています。
Pythonの回数指定ループの基本
回数指定ループとは何かをPythonで理解する

Pythonでの回数指定ループとは、「あらかじめ決めた回数だけ、同じ処理をくり返すループ」のことです。
たとえば「10回だけログを出す」「3回だけAPIをリトライする」「100件ごとにバッチ処理する」といった場面で使います。
Pythonでは、条件が真のあいだ回し続けるwhileループもありますが、「回数があらかじめ決まっているならfor + rangeを使う」のが基本です。
コードの意図が明確になり、バグも入りにくくなります。
for文とrange関数の基本構文

Pythonで回数指定ループを書く基本形は次の通りです。
# 基本形: rangeとfor文
for 変数 in range(回数):
繰り返したい処理を書く
range(回数)は、0から回数 - 1までの整数を順番に生成します。
ループ変数には、その整数が1つずつ入っていきます。
# 5回繰り返す基本例
for i in range(5):
print("ループ回数:", i)
ループ回数: 0
ループ回数: 1
ループ回数: 2
ループ回数: 3
ループ回数: 4
このように、「0から始まる」「終了値は含まれない」というルールがとても重要です。
このルールを理解しておけば、あらゆる回数指定ループを正しく書けるようになります。
rangeの引数(start, stop, step)の意味と使い分け

rangeには、最大で3つの引数を渡せます。
range(stop)
0, 1, 2, …, stop-1 までrange(start, stop)
start, start+1, …, stop-1 までrange(start, stop, step)
start, start+step, start+2*step, … と進み、stopに達する前で止まる
次のコードで動きを確認してみます。
# range(stop) の例
for i in range(3):
print(i, end=" ")
print() # 改行
# range(start, stop) の例
for i in range(2, 5):
print(i, end=" ")
print()
# range(start, stop, step) の例
for i in range(0, 10, 3):
print(i, end=" ")
print()
0 1 2
2 3 4
0 3 6 9
最も重要なのは「stopは含まれない」という点です。
「1からnまで」を書きたいときにrange(1, n)としてしまい、nが含まれないというミスはよくあるので注意が必要です。
rangeとfor文の基本パターン10選
rangeで0からn-1までループする基本パターン

最もよく使うのがrange(n)で、0からn-1までを回すパターンです。
n = 5
for i in range(n):
# i は 0, 1, 2, 3, 4 と変化する
print(f"{i} 回目の処理です")
0 回目の処理です
1 回目の処理です
2 回目の処理です
3 回目の処理です
4 回目の処理です
回数だけが重要で、iの値は使わない場合、慣習的に_を使うこともあります。
# 3回だけメッセージを表示するが、カウンタは使わない
for _ in range(3):
print("Hello")
range(1, n+1)で1からnまでループするパターン

人間が数える回数や、番号付け(1番, 2番, …)のように1から始まる数列を扱いたい場合は、range(1, n+1)を使います。
n = 5
for i in range(1, n + 1):
print(f"{i}人目のユーザーを処理します")
1人目のユーザーを処理します
2人目のユーザーを処理します
3人目のユーザーを処理します
4人目のユーザーを処理します
5人目のユーザーを処理します
「1からnまで」はrange(1, n+1)と、ほぼお決まりのパターンとして覚えておくとよいです。
マイナス方向にカウントダウンするループ

stepに負の値を指定すると、カウントダウンするループになります。
# 5,4,3,2,1 とカウントダウンする
for i in range(5, 0, -1):
print(i, end=" ")
print()
5 4 3 2 1
0まで含めたい場合はrange(5, -1, -1)のように、stopより1つ小さい値を指定します。
# 5,4,3,2,1,0 とカウントダウン
for i in range(5, -1, -1):
print(i, end=" ")
print()
5 4 3 2 1 0
step付きrangeで一定間隔ごとにループする

step引数を使うと、一定間隔で値を進めることができます。
# 0, 2, 4, 6, 8 と2刻みでループ
for i in range(0, 10, 2):
print(i, end=" ")
print()
0 2 4 6 8
3つおき、5つおきなども同様に書けます。
たとえば「ページ番号を10件ごとに増やす」ような場面で便利です。
# 0, 10, 20, 30 と10刻みでループ
for offset in range(0, 40, 10):
print(f"{offset}件目から10件取得します")
0件目から10件取得します
10件目から10件取得します
20件目から10件取得します
30件目から10件取得します
ネストしたfor文とrangeで二重ループを作る

二重ループで、表形式のデータや座標を扱うことができます。
# 3行3列の座標を出力する例
for i in range(3): # 行方向のループ
for j in range(3): # 列方向のループ
print(f"({i}, {j})", end=" ")
print() # 行が終わったら改行
(0, 0) (0, 1) (0, 2)
(1, 0) (1, 1) (1, 2)
(2, 0) (2, 1) (2, 2)
このように「外側が行、内側が列」という形で書くのが定番です。
lenとrangeでリストのインデックスを回す

リストの要素を、インデックスを使って処理したい場合はlenとrangeを組み合わせます。
fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
print(i, fruits[i])
0 apple
1 banana
2 cherry
インデックスと値の両方を使いたいときには、後述するenumerateの利用も検討するとよいです。
enumerateとrangeの違いと使い分け

enumerateは、シーケンスをループしながらインデックスと値を同時に取り出すための組み込み関数です。
fruits = ["apple", "banana", "cherry"]
# range + len
for i in range(len(fruits)):
print(i, fruits[i])
# enumerate を使う
for i, fruit in enumerate(fruits):
print(i, fruit)
0 apple
1 banana
2 cherry
0 apple
1 banana
2 cherry
単にリストを前から順に処理するだけならfor x in list、インデックスも欲しいならenumerate、カスタムなインデックス制御(逆順、スキップなど)が必要なときにrangeという使い分けがおすすめです。
breakとcontinueを使った回数指定ループ制御

breakとcontinueは、ループの流れを制御するためのキーワードです。
# break の例: 先に見つかった偶数で終了
for i in range(10):
if i % 2 == 0 and i != 0:
print("最初に見つかった偶数:", i)
break # ループを抜ける
最初に見つかった偶数: 2
# continue の例: 偶数だけを処理する
for i in range(1, 6):
if i % 2 != 0:
continue # 奇数のときは処理をスキップ
print("偶数です:", i)
偶数です: 2
偶数です: 4
「ループを打ち切るか」「その周だけスキップするか」を明確に意識して使い分けることが大切です。
for-else構文とrangeの組み合わせ

Python独特の構文としてfor-elseがあります。
ループがbreakされずに最後まで実行された場合にelseブロックが実行されます。
# 素数探索の簡易例: n が素数かどうか調べる
n = 17
for i in range(2, n):
if n % i == 0:
print(f"{n} は {i} で割り切れるので素数ではありません")
break
else:
# break されなかったときだけ実行される
print(f"{n} は素数です")
17 は素数です
「見つかればbreak、見つからなければ何かする」という処理に、for-elseはとてもよく合います。
rangeとsum・min・maxなど組み込み関数の連携

rangeオブジェクトは、そのままsumやmin、maxなどの組み込み関数に渡せます。
numbers = range(5) # 0,1,2,3,4
print("合計:", sum(numbers))
print("最小:", min(numbers))
print("最大:", max(numbers))
合計: 10
最小: 0
最大: 4
また、内包表記と組み合わせれば、計算結果を集計することもできます。
# 1〜10までの平方数の合計
squared_sum = sum(i * i for i in range(1, 11))
print("1〜10の平方数の合計:", squared_sum)
1〜10の平方数の合計: 385
実務で使えるrangeとfor文の応用パターン10選
rangeとfor文で指定回数のリトライ処理を実装する

ネットワーク処理などでは、「最大n回までリトライ」というパターンがよく登場します。
import random
MAX_RETRY = 3
def call_api():
"""成功したり失敗したりするダミーAPI"""
if random.random() < 0.5:
raise ConnectionError("接続に失敗しました")
return "OK"
for attempt in range(1, MAX_RETRY + 1):
try:
print(f"{attempt}回目のAPI呼び出し")
result = call_api()
print("成功:", result)
break # 成功したらループ終了
except ConnectionError as e:
print("失敗:", e)
if attempt == MAX_RETRY:
print("最大リトライ回数に達したため中止します")
1回目のAPI呼び出し
失敗: 接続に失敗しました
2回目のAPI呼び出し
成功: OK
「何回まで試すか」をrangeで明示することで、コードの意図がわかりやすくなります。
rangeを使ったバッチ処理や分割実行のループ

大量データを処理するとき、一定件数ずつ分割して処理するバッチはよく使われます。
data = list(range(1, 101)) # 1〜100のダミーデータ
batch_size = 20
for start in range(0, len(data), batch_size):
end = min(start + batch_size, len(data))
batch = data[start:end]
print(f"{start}〜{end-1}件目を処理します: {batch}")
0〜19件目を処理します: [1, 2, 3, ..., 20]
20〜39件目を処理します: [21, 22, 23, ..., 40]
40〜59件目を処理します: [41, 42, 43, ..., 60]
60〜79件目を処理します: [61, 62, 63, ..., 80]
80〜99件目を処理します: [81, 82, 83, ..., 100]
startをrange(0, 長さ, バッチサイズ)で動かすのが典型的な書き方です。
インデックスと値を同時に扱うfor文パターン

実務では「内部的には0始まりのインデックスを使いたいが、表示は1番からにしたい」というケースが多くあります。
users = ["Alice", "Bob", "Charlie"]
# 表示は1番から、内部的な列挙にはインデックスも使う
for display_no, name in enumerate(users, start=1):
print(f"{display_no}番目のユーザー: {name}")
1番目のユーザー: Alice
2番目のユーザー: Bob
3番目のユーザー: Charlie
表示番号はenumerate(..., start=1)で管理し、必要なら別にrangeでインデックスを扱うという分け方もできます。
2次元配列(リストのリスト)をrangeで走査する

2次元リスト(リストのリスト)は、外側のrangeで行、内側のrangeで列を回すのが基本です。
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
rows = len(matrix)
cols = len(matrix[0])
for i in range(rows):
for j in range(cols):
value = matrix[i][j]
print(f"matrix[{i}][{j}] = {value}")
matrix[0][0] = 1
matrix[0][1] = 2
matrix[0][2] = 3
matrix[1][0] = 4
matrix[1][1] = 5
matrix[1][2] = 6
matrix[2][0] = 7
matrix[2][1] = 8
matrix[2][2] = 9
行ごと・列ごとの集計なども、この形から応用できます。
rangeでID番号や連番を生成するループ

ユーザーIDや注文番号など、連番のIDを生成する場面ではrangeがそのまま使えます。
# user_001 〜 user_010 までのIDを生成
for i in range(1, 11):
user_id = f"user_{str(i).zfill(3)}" # 3桁ゼロ埋め
print(user_id)
user_001
user_002
user_003
user_004
user_005
user_006
user_007
user_008
user_009
user_010
「何番から何番まで」のIDを作る処理は、実務で頻出するパターンです。
rangeとzipで複数シーケンスを並行して回す

複数のリストを同時にループするにはzipが便利です。
rangeでインデックスを回す書き方と比較してみます。
names = ["Alice", "Bob", "Charlie"]
scores = [80, 90, 70]
# range + インデックスで書く場合
for i in range(len(names)):
print(names[i], scores[i])
# zip を使う場合
for name, score in zip(names, scores):
print(name, score)
Alice 80
Bob 90
Charlie 70
Alice 80
Bob 90
Charlie 70
インデックスが特別な意味を持たない場合はzipを使うと、コードが読みやすくなります。
rangeを使ったテストデータの大量生成

開発やテストでは、大量のダミーデータを簡単に生成したいことがよくあります。
import random
test_users = []
for i in range(1, 101):
user = {
"id": i,
"name": f"user_{str(i).zfill(3)}",
"score": random.randint(0, 100),
}
test_users.append(user)
# 先頭5件だけ確認
for user in test_users[:5]:
print(user)
{'id': 1, 'name': 'user_001', 'score': 57}
{'id': 2, 'name': 'user_002', 'score': 83}
{'id': 3, 'name': 'user_003', 'score': 12}
{'id': 4, 'name': 'user_004', 'score': 99}
{'id': 5, 'name': 'user_005', 'score': 41}
「テストコードの頭に、rangeでダミーデータを作る」というのは、実務では定番のテクニックです。
rangeとfor文で進捗ログやプログレスを表示する

長時間かかる処理では、どこまで進んだかをログで表示すると使い勝手が良くなります。
import time
total = 20
for i in range(1, total + 1):
# ダミーで時間がかかる処理
time.sleep(0.05)
if i % 5 == 0 or i == total:
progress = i / total * 100
print(f"進捗: {i}/{total} ({progress:.1f}%)")
進捗: 5/20 (25.0%)
進捗: 10/20 (50.0%)
進捗: 15/20 (75.0%)
進捗: 20/20 (100.0%)
専用ライブラリ(例: tqdm)と組み合わせる場合も、rangeがベースになります。
rangeとリスト内包表記で簡潔な回数指定処理を書く

リスト内包表記は、「ループしながらリストを作る」処理を短く書ける構文です。
# 通常のfor文で平方数リストを作る
squares = []
for i in range(1, 6):
squares.append(i * i)
print(squares)
# リスト内包表記で書く
squares2 = [i * i for i in range(1, 6)]
print(squares2)
[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]
条件付きで絞り込みたいときも、内包表記の中にifを書けます。
# 1〜20のうち、偶数の平方数だけをリストにする
even_squares = [i * i for i in range(1, 21) if i % 2 == 0]
print(even_squares)
[4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
大きなrangeを扱うときのパフォーマンスとメモリの注意点

range自体はイテラブル(必要になったときに次の値を計算するオブジェクト)なので、range(1_000_000_000)のような大きな値を指定しても、即座にリストがメモリ上に展開されるわけではありません。
問題になるのは、list(range(...))のようにrangeをリストに変換してしまう場合です。
# 注意: 実際に1億は環境によってメモリ不足になる可能性があります
n = 100_000
r = range(n) # これは軽い
lst = list(range(n)) # これは n 個の要素分メモリを使う
print(type(r), type(lst))
print("rangeの長さ:", len(r))
print("listの長さ:", len(lst))
<class 'range'> <class 'list'>
rangeの長さ: 100000
listの長さ: 100000
「全要素を同時にメモリに持つ必要があるか」を考え、不要なら安易にリスト化しないことがパフォーマンス面では重要です。
Pythonの回数指定ループを使いこなすコツ
while文との違いから見るfor文(range)の適切な使いどころ

whileとforの違いは次のように整理できます。
| 特徴 | for + range | while |
|---|---|---|
| 目的 | 回数が決まったループ | 条件が変化するまで続くループ |
| カウンタ | 自動で管理される | 手動で増減させる必要がある |
| 典型例 | 3回リトライ、100件ループ | ユーザー入力待ち、キューが空になるまで |
「何回まわすかがはっきりしている → for + range」「そうでない → while」という基準で選ぶと、読みやすく、安全なコードになります。
読みやすいrangeとfor文を書くためのスタイルガイド

読みやすいrangeの書き方には、いくつかのコツがあります。
1つ目は変数名を意味のあるものにすることです。
# 悪い例
for i in range(1, 13):
print(i)
# 良い例: month という名前にする
for month in range(1, 13):
print(f"{month}月")
2つ目はマジックナンバーを避け、定数や変数を使うことです。
# 悪い例
for i in range(0, 100, 20):
...
# 良い例
BATCH_SIZE = 20
MAX_ITEMS = 100
for start in range(0, MAX_ITEMS, BATCH_SIZE):
...
3つ目は0始まりか1始まりかを意識してコメントすることです。
# 1〜n まで: ユーザー向け番号
for idx in range(1, n + 1):
...
# 0〜n-1 まで: 内部インデックス
for index in range(n):
...
実務コードでありがちなrangeの落とし穴と対策

最後に、rangeでよくあるミスと、その回避方法をまとめます。
- stopを含むと勘違いする
例: 「1〜nまで」と思ってrange(1, n)と書いてしまう。
対策:「stopは含まれない」を常に意識し、1〜nならrange(1, n+1)と書く。 - 負のstepで終わりに到達しない
例:range(0, 10, -1)のように、start < stop なのにstepが負。
対策: カウントダウンのときはstart > stop、step < 0となるように整理する。 - 巨大なrangeをリスト化してメモリを使い切る
例:list(range(100_000_000))など。
対策: 本当にリストが必要か検討し、可能ならrangeのまま処理する。
これらを意識しておけば、rangeを安全に、安心して使いこなせるようになります。
まとめ
Pythonの回数指定ループは、for文とrange関数を正しく理解することで、シンプルかつ強力な表現が可能になります。
0〜n-1と1〜nの書き分け、start/stop/stepの意味、breakやfor-elseとの組み合わせ、さらにバッチ処理やリトライ、テストデータ生成などの実務パターンを身につければ、多くの処理を安全で読みやすいコードで書けます。
日々の開発で意識的にrangeのパターンを使い分けることで、自然とPythonicなループ設計ができるようになっていきます。
