for文を内包表記に書き換える方法|Pythonのリスト内包表記のif・if-else・2重ループを実行例で解説

for文の内包表記(リスト内包表記)は、「空のリストを作り、for文で1つずつappendする」コードを[式 for 変数 in 繰り返す対象]の1行に書き換える書き方です。for文の中身の式を先頭に出し、forの行をその後ろに続ける、と覚えると書き換えられます。

この記事では、for文と内包表記を並べて、書き換えの手順を型ごとに説明します。コードはすべてPython 3.14.8で実行し、実行結果をそのまま載せています。

for文を内包表記に書き換える手順

# for文
squares = []
for n in range(1, 6):
    squares.append(n * n)
print(squares)

# 内包表記
squares = [n * n for n in range(1, 6)]
print(squares)

実行結果:

[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]

書き換えは次の3ステップです。

  1. squares = []とappendを消し、全体を[ ]で囲む
  2. append()の中身(n * n)を先頭に書く
  3. その後ろにfor n in range(1, 6)をそのまま続ける(末尾の:は付けない)

Pythonの公式チュートリアルでも、リストの内包表記は、各要素に操作をした結果のリストや、条件を満たす要素だけのリストを簡潔に作る手段として説明されています。

for文の形内包表記
全部を変換してappend[式 for x in 対象]
ifで絞ってappend[式 for x in 対象 if 条件]
if-elseで値を変えてappend[式A if 条件 else 式B for x in 対象]
for文が2重[式 for x in 外側 for y in 内側]
辞書に追加{キー: 値 for x in 対象}
集合に追加{式 for x in 対象}

if を入れる位置は2通り

scores = [72, 45, 88, 59, 91]

# 条件に合うものだけ残す(if は後ろ)
passed = [s for s in scores if s >= 60]
print(passed)

# すべての要素を変換し、値を条件で分ける(if-else は前)
labels = ["合格" if s >= 60 else "不合格" for s in scores]
print(labels)

実行結果:

[72, 88, 91]
['合格', '不合格', '合格', '不合格', '合格']

いちばん間違えやすいのがここです。要素を絞り込むifはforの後ろ、値を切り替えるif-elseはforの前に書きます。前者は「条件に合わないものは入れない」ので要素の数が減り、後者は「すべての要素について、どちらかの値を入れる」ので要素の数は変わりません。後ろのifにelseを付けると文法エラーになります。

scores = [72, 45, 88]
result = [s for s in scores if s >= 60 else 0]

実行結果:

  File "ex8.py", line 2
    result = [s for s in scores if s >= 60 else 0]
                                           ^^^^
SyntaxError: invalid syntax

前に書くA if 条件 else Bは「三項演算子(条件式)」と呼ばれる書き方で、詳しくはPythonの三項演算子の書き方で扱っています。

for文が2重のときは、上から順に左へ並べる

# for文が2重のとき:書く順番は for文と同じ(外側が先)
pairs = []
for color in ["赤", "青"]:
    for size in ["S", "M"]:
        pairs.append(color + size)
print(pairs)

pairs = [color + size for color in ["赤", "青"] for size in ["S", "M"]]
print(pairs)

# 2次元リストを1次元にする
matrix = [[1, 2, 3], [4, 5, 6]]
flat = [x for row in matrix for x in row]
print(flat)

# 2次元リストの行と列を入れ替える(内包表記の中に内包表記)
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed)

実行結果:

['赤S', '赤M', '青S', '青M']
['赤S', '赤M', '青S', '青M']
[1, 2, 3, 4, 5, 6]
[[1, 4], [2, 5], [3, 6]]

2重のfor文を書き換えるときは、外側のforを先に、内側のforを後に書きます。for文を上から読んだ順番がそのまま左から右に並ぶ、と覚えると迷いません。一方、行と列を入れ替える例のように[[...] for i in ...]と内包表記の中に内包表記を入れると、意味が変わります(外側の内包表記が1行ずつリストを作る)。3重以上になる場合や、条件が複雑になる場合は、for文のまま書いたほうが読みやすくなります。

辞書・集合・ジェネレータ式

words = ["apple", "banana", "cherry", "apple"]

lengths = {w: len(w) for w in words}       # 辞書内包表記
initials = {w[0] for w in words}           # 集合内包表記(重複は消える)
total = sum(len(w) for w in words)         # ジェネレータ式(リストを作らない)

print(lengths)
print(sorted(initials))
print(total)

実行結果:

{'apple': 5, 'banana': 6, 'cherry': 6}
['a', 'b', 'c']
22

{キー: 値 for ...}は辞書、{式 for ...}は集合(重複が消える)になります。sum()などに直接渡す場合は、[ ]を外した(式 for ...)(ジェネレータ式)にすると、途中のリストを作らずに済みます。リストとの違いやメモリの比較はPythonのジェネレータ式とは?で実行結果つきで説明しています。

内包表記にしないほうがよい場面

# 内包表記に書き換えないほうがよい例:途中で止めたい・副作用が主目的
items = [3, 8, -1, 5]
for x in items:
    if x < 0:
        print("負の数が見つかったので中止:", x)
        break
    print("処理:", x)

# print のためだけに内包表記を使うと、不要な None のリストができる
result = [print(x) for x in [1, 2]]
print(result)

実行結果:

処理: 3
処理: 8
負の数が見つかったので中止: -1
1
2
[None, None]
  • 途中で止めたいとき:内包表記にはbreakを書けない。見つかった時点で止めたい処理はfor文で書く
  • 表示や保存など「作業」が目的のとき:内包表記はリストを作るための書き方。print()のために使うと、上のようにNoneだけのリストができてしまう
  • 1行が長くなるとき:条件が2つ以上、forが3重以上などで1行に収まらないなら、for文のほうが読み手に親切

for文より速い?実際に測ってみる

同じリストを作る2つの書き方を、標準ライブラリのtimeitで比べました。

import timeit

def with_for():
    result = []
    for n in range(10_000):
        result.append(n * 2)
    return result

def with_comp():
    return [n * 2 for n in range(10_000)]

assert with_for() == with_comp()
for f in (with_for, with_comp):
    t = min(timeit.repeat(f, number=200, repeat=5))
    print(f"{f.__name__}: {t / 200 * 1000:.3f} ミリ秒/回")

実行結果:

with_for: 0.228 ミリ秒/回
with_comp: 0.204 ミリ秒/回

この環境(Apple M2、Python 3.14.8)では、内包表記のほうが1割ほど速い結果でした。appendメソッドを毎回呼び出す手間がないぶん速くなりやすいものの、差は環境や処理の中身で変わり、1万件で0.02ミリ秒程度です。速さよりも「リストを作っている」ことが一目で分かる読みやすさを理由に使うのがよいでしょう。時間の測り方はPythonのtimeitで処理時間を測る方法にまとめています。

内包表記の変数は外に漏れない

x = "外側の x"
squares = [x * x for x in range(3)]
print(squares)
print(x)  # 内包表記の中の x は外に漏れない

実行結果:

[0, 1, 4]
外側の x

Python 3では、内包表記の中で使ったループ変数は内包表記の外に影響しません。for文の場合はループが終わったあとも変数が残るので、ここは両者の違いです。なお、Python 3.12ではPEP 709(Comprehension inlining)により、リスト・辞書・集合の内包表記の内部の実装が変わりましたが、この「外に漏れない」動きは変わっていません。

練習問題と解答

  1. 1から10までの数のうち、5で割り切れる数のリストを作る
  2. ["apple", "banana", "cherry"]の各要素の文字数を辞書にする
  3. 1から20までの素数の集合を作る

解答例です。

# 1. 1から10までのうち、5で割り切れる数
print([n for n in range(1, 11) if n % 5 == 0])

# 2. 各単語の文字数の辞書
print({w: len(w) for w in ["apple", "banana", "cherry"]})

# 3. 1から20までの素数の集合(2からn-1までのどれでも割り切れない数)
primes = {n for n in range(2, 21) if all(n % d != 0 for d in range(2, n))}
print(sorted(primes))

実行結果:

[5, 10]
{'apple': 5, 'banana': 6, 'cherry': 6}
[2, 3, 5, 7, 11, 13, 17, 19]

3問目は、all()にジェネレータ式を渡して「2からn-1までのどの数でも割り切れない」を判定しています。for文そのものの基本から復習したい場合はfor文とwhile文の違いをどうぞ。

よくある質問

Q. リスト内包表記でif-elseを使うには?

値を切り替えるif-elseは、forの前に書き、[A if 条件 else B for x in 対象] の形にします。forの後ろに書くifは絞り込み用で、elseは付けられません。

Q. 2重のfor文を内包表記にするときの順番は?

for文を上から読んだ順に、左から右へ並べます。外側のforを先に、内側のforを後に書きます。

Q. 内包表記はfor文より速いですか?

多くの場合わずかに速くなります。この記事の計測(Apple M2、Python 3.14.8、1万件)では約1割速い結果でしたが、差は環境や処理の内容で変わります。

Q. 内包表記でbreakは使えますか?

使えません。条件に当たった時点で処理を止めたい場合は、for文で書きます。

あわせて読みたい

学習の進め方はPython学習のロードマップ、職種ごとの違いはエンジニアの職種の違いにまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら