
Table of Contents(目次)
1. 2重ループの基本
まずは、2重ループの基本構造を確認しましょう。Pythonでは、for
やwhile
を入れ子にして記述することで、1つのループの中でさらに繰り返し処理を行うことができます。これを「ループのネスト(入れ子)」と呼びます。
次の例では、3×3の表を作るように、外側ループと内側ループを組み合わせて出力します。
for i in range(3):
for j in range(3):
print(f'(i[{i}], j[{j}])', end=' ')
print()
出力結果:
(i[0], j[0]) (i[0], j[1]) (i[0], j[2])
(i[1], j[0]) (i[1], j[1]) (i[1], j[2])
(i[2], j[0]) (i[2], j[1]) (i[2], j[2])

このとき、i
とj
の変化を表で整理すると、次のようになります。
外側ループ { i } の変化 | 内側ループ { j } の変化 |
---|---|
0 | 0 → 1 → 2 |
1 | 0 → 1 → 2 |
2 | 0 → 1 → 2 |
つまり、外側ループの1回の繰り返しごとに、内側ループがすべての回数を実行しています。外側ループの先頭に処理が戻り、再度内側ループ(for j in range(3):
)が実行される時には、range(3)の処理は最初からスタートします。(0 → 1 → 2
と処理が進みます。)
次に、listを使った2重ループの例を見てみましょう。
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
for row in matrix:
for value in row:
print(value, end=' ')
print()
出力結果:
1 2 3
4 5 6
7 8 9
処理の流れを表で見てみましょう。
row の値 | value の変化 |
---|---|
[1, 2, 3] | 1 → 2 → 3 |
[4, 5, 6] | 4 → 5 → 6 |
[7, 8, 9] | 7 → 8 → 9 |
外側ループでは行(row)を1つずつ取り出し、内側ループではその行の中の要素を1つずつ処理しています。
次に、while
ループを使ったネストの例です。
i = 0
while i < 3:
j = 0
while j < 3:
print(i, j)
j += 1
i += 1
while
ループの場合、内側ループに対応したカウンター(変数j
)も用意するなど、無限ループにならないように配慮する必要があります。
これまで見てきたように、for
とfor
のネストや、while
とwhile
のネスト以外にも、外側ループと内側ループの性質に基づいて、固定回数の反復を行うループとそうでないループを組み合わせたい場合は、for
とwhile
を組み合わせることができます。
2. ループ制御の基本(break / continue)
2重ループでは、break
やcontinue
を使って繰り返しを制御することができます。ただし、それらが「内側ループ」か「外側ループ」に作用するかを理解しておくことが大切です。
2.1 breakの挙動
次の例では、内側ループでbreak
を使っています。
for i in range(3):
for j in range(3):
if j == 1:
break
print(i, j)
出力結果:
0 0
1 0
2 0
変数j
が1
の時にbreak
するので(break
するとprint(i, j)
は実行されない)、変数j
が0
の時のprintのみ出力されます。break
は内側ループだけを終了させ、外側ループは続行します。(変数i
は0 → 1 → 2
と処理が進みます。)
次の例は、外側ループでbreak
を使っています。
for i in range(3):
if i == 1:
break
for j in range(3):
print(i, j)
出力結果:
0 0
0 1
0 2
変数i
が1
の時にbreak
するので、変数i
が0
の時のprintのみ出力されます。外側ループでbreak
されると、ループ全体が終了します。
2.2 continueの挙動
continue
を使うと、現在のループの1回分をスキップします。
for i in range(3):
for j in range(3):
if j == 1:
continue
print(i, j)
出力結果:
0 0
0 2
1 0
1 2
2 0
2 2
内側ループでcontinue
された場合は、その回のprint(i, j)
は実行されずにfor j in range(3):
の先頭に処理が戻ります。ですので、変数j
が1
の時以外の情報が出力されています。
次のループは、外側ループでcontinue
されています。
for i in range(3):
if i == 1:
continue
for j in range(3):
print(i, j)
出力結果:
0 0
0 1
0 2
2 0
2 1
2 2
変数i
が1
の時にcontinue
により外側ループの先頭に戻るので、変数i
が1
以外の時のみprint出力されます。
3. 変数スコープと値の更新
ネストされたループでは、変数のスコープ(有効範囲)にも注意が必要です。Pythonでは、ループ変数は同じスコープ(関数内やスクリプト全体)に存在します。そのため、同じスコープの中で使用される他の変数の値を誤って上書きしたり、予期せぬ値を参照しないように気を付けましょう。
(スコープについては専用のレクチャーで学習します。)
for i in range(2):
for j in range(3):
print(i, j)
print('ループ後のi:', i)
print('ループ後のj:', j)
出力結果:
0 0
0 1
0 2
1 0
1 1
1 2
ループ後のi: 1
ループ後のj: 2
このように、ループが終了したあとでも、変数iとjは残っています。予期せぬ値を参照してしまうミスを防ぐため、スコープを意識することが重要です。
4. インデントと可読性、ネストの落とし穴
2重ループでは、インデントが1段深くなるため、可読性が下がりやすくなります。適切な空行やコメントを入れて、構造を明確にしましょう。
また、更にネストさせて3重ループ、4重ループと増やすこともできますが、ネストさせればさせるほど可読性が下がり、バグが紛れる可能性も高まります。大量の繰り返し処理が必要な場合は、機能ごとに関数に分けて管理するなど、ネストが深くなりすぎないように工夫してコードを書くようにしましょう。
(関数については専用のレクチャーで学習します。)
for i in range(2):
print(f'iのループ開始: {i}')
for j in range(2):
print(f' jの処理: {j}')
for k in range(3):
if k == 2:
continue
print(f' kの処理: {k}')
print(f'i({i})のループ終了')
出力結果:
iのループ開始: 0
jの処理: 0
kの処理: 0
kの処理: 1
jの処理: 1
kの処理: 0
kの処理: 1
i(0)のループ終了
iのループ開始: 1
jの処理: 0
kの処理: 0
kの処理: 1
jの処理: 1
kの処理: 0
kの処理: 1
i(1)のループ終了
5. 無限ループと終了条件の確認
while
ループをネストする場合、終了条件を誤ると無限ループに陥ることがあります。
i = 0
while i < 3:
j = 0
while j < 3:
print(i, j)
# 'j += 1' を忘れてしまうと無限ループになってしまう
i += 1 # 'i += 1'も忘れずに
変数i
とj
を両方とも更新しないと、永遠に同じ処理が繰り返されてしまうため注意が必要です。