閉じる

for文を置き換えるPythonリスト内包表記の使い方とコツ

Pythonのリスト内包表記は、反復処理と条件分岐を1行で表現できる便利な書き方です。

ループとappend()を置き換えることでコードが短く読みやすくなります。

ただし、やみくもに1行化すると読みづらくなるため、「正しい位置にifを書く」「読み順を意識する」「多機能を詰め込みすぎない」といった基本の型を身につけることが大切です。

Pythonを扱う求人
読み込み中…

リスト内包表記とは?for文を1行にする基本

Pythonリスト内包表記の基本構文と読み方

リスト内包表記の基本形は次のとおりです。

「左が作りたい値、右がデータの流れ」という視点で読みます。

  • 構文: [ 式 for 変数 in イテラブル if 条件 ]
  • 読み方: 「イテラブルから変数を1つずつ取り出し、条件を満たすときだけ式を評価してリストに入れる」
Python
# 0〜9の平方リストを作る基本例
squares = [n * n for n in range(10)]
print(squares)
実行結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

ポイントは、式(作りたい値)が最初に来ることです。

これにより「何を作るか」が先に見え、読み手は意図を素早く理解できます。

for文+appendをリスト内包表記に置き換える(例)

従来の書き方と1行化の対応を具体的に見てみます。

Python
# 従来: for + append
evens = []
for n in range(10):
    if n % 2 == 0:
        evens.append(n)
print(evens)

# 1行: リスト内包表記
evens_comp = [n for n in range(10) if n % 2 == 0]
print(evens_comp)
実行結果
[0, 2, 4, 6, 8]
[0, 2, 4, 6, 8]

「フィルタ条件のifは末尾に置く」のが正しい形です。

この位置関係は読みやすさにも直結します。

可読性を上げる読み順のコツ

  • 読む順番: 「for句 → if(あれば) → 左端の式」
  • 書く順番: 実装時は左から書かず、まずforifでデータの流れを決め、その後に左端の式(作る値)を書くと迷いません。
  • if-elseの条件式は左側に置くのがルールです(詳細は後述)。

読み方の例

[式 for x in xs if 条件]は「xsを走査し、条件を満たすxに対して式を評価して集める」と読みます。

慣れると自然に理解できるようになります。

条件付きリスト内包表記の使い方

ifで要素をフィルタリングする書き方

フィルタはforの直後、末尾にifを置きます。

Python
# 偶数のみを抽出
evens = [n for n in range(20) if n % 2 == 0]
print(evens)
実行結果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

「フィルタのifは末尾」という位置は必ず守ります。

これを崩すとPythonの構文上エラーになります。

if-elseで値を切り替える書き方

条件によって「入れる値」を変えたい場合は、if-elseの「条件演算子」を左端の式の位置に置きます。

Python
# 奇数偶数のラベルをつける
labels = ["even" if n % 2 == 0 else "odd" for n in range(5)]
print(labels)
実行結果
['even', 'odd', 'even', 'odd', 'even']
注意

フィルタのifと条件演算子のif-elseは役割も場所も異なります

  • フィルタ: [式 for x in xs if 条件] (要素を入れるか捨てるか)
  • 条件演算子: [値A if 条件 else 値B for x in xs] (入れる値を切り替える)

複数条件(and/or)の書き方と注意点

複合条件はandorを使います。

曖昧さを避けるため括弧で明示しましょう。

Python
# 5以上かつ偶数、または0の要素だけ
filtered = [n for n in range(10) if (n >= 5 and n % 2 == 0) or n == 0]
print(filtered)
実行結果
[0, 6, 8]

論理演算子の優先順位は「not > and > or」です。

迷ったら必ず括弧で意図を固定すると安全です。

ネストしたリスト内包表記とパターン

二重forの書き方と評価順

ネストはforを並べるだけです。

右側のforが「内側のループ」として速く回ります。

Python
# 2つのリストから順序つきペアを作る
A = ["a", "b"]
B = [1, 2, 3]
pairs = [(x, y) for x in A for y in B]  # xが外側、yが内側
print(pairs)
実行結果
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3)]

読み方は「for x in A → for y in B → (x, y)を作る」です。

順序が結果に影響するため、外側と内側を取り違えないようにします。

2次元リストを平坦化する(flatten)

典型パターンは「行を先に回し、次に各行の要素を回す」です。

Python
# 2次元リストを1次元に平坦化
matrix = [[1, 2, 3], [4, 5], [6]]
flat = [x for row in matrix for x in row]
print(flat)
実行結果
[1, 2, 3, 4, 5, 6]

「for row … for x in row」の語順が定石です。

条件付きのネスト(例)

ネストに条件を組み合わせると、柔軟な抽出が可能です。

Python
# 2次元リストから偶数だけを平坦化して抽出
matrix = [[1, 2, 3], [4, 5, 6]]
even_flat = [x for row in matrix for x in row if x % 2 == 0]
print(even_flat)

# 掛け算表(1〜3)のうち積が偶数のペアのみ残す
pairs = [(i, j) for i in range(1, 4) for j in range(1, 4) if (i * j) % 2 == 0]
print(pairs)
実行結果
[2, 4, 6]
[(1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (3, 2)]
注意

フィルタは「一番右に近い位置」で評価されます。

上の例ではxが定義済みの位置(右端)にifを置いています。

初心者向けコツと注意点(リスト内包表記)

1行に詰め込みすぎない可読性ルール

リスト内包表記は短く書けますが、「短いほど良い」わけではありません

目安は次のとおりです。

  • 最大で二重for、フィルタは1〜2個までに留める
  • 行が長くなるなら括弧で折り返す(PEP 8の行長目安は79文字、プロジェクトによっては88文字)
Python
# 長くなる場合は括弧で折り返して可読性を確保
result = [
    f"{user}:{score}"
    for user, score in users_scores
    if score >= 80
]
print(result)  # 実際の値は users_scores に依存

上のように縦方向の揃えを活用すると、データの流れが一目で分かるため保守が容易になります。

副作用のある処理は避ける(printやappend)

アンチパターンとして、副作用(出力、書き込み、ミューテーション)のために内包表記を使うのは避けます。

Python
# 悪い例: 目的が「副作用」だけ
_ = [print(n) for n in range(3)]  # 実行はできるが非Pythonic

# 良い例: 副作用が目的なら通常のforを使う
for n in range(3):
    print(n)
実行結果
0
1
2
0
1
2

上の出力のように、悪い例は「作られた無駄なリスト」と「出力」が二重に混在します。

内包表記は「新しいリストを作る」ための道具として使い分けましょう。

変数スコープと名前衝突に注意

Python 3では、リスト内包表記のループ変数は内包表記のスコープ内に閉じます

外側の同名変数を汚染しません。

Python
x = 100
vals = [x * 2 for x in range(3)]  # このxは内包表記の中だけ有効
print(vals)
print(x)  # 外側のxは変わらない
実行結果
[0, 2, 4]
100

ただし、外側の変数名と同じ名前を使うと可読性は下がるため、意図的に避けるのが無難です。

パフォーマンスの目安とfor文の使い分け

内包表記はPythonレベルのループよりも低オーバーヘッドで、一般に同等のfor文+appendより速いことが多いです。

ただし、可読性が落ちるほど複雑なら通常のforを選びます。

Python
# 簡易ベンチマーク(timeitは実行環境で結果が変わります)
import timeit

setup = "nums = list(range(10000))"
stmt_for = """
res = []
for n in nums:
    if n % 2 == 0:
        res.append(n * 2)
"""
stmt_comp = "[n * 2 for n in nums if n % 2 == 0]"

t_for = timeit.timeit(stmt_for, setup=setup, number=500)
t_comp = timeit.timeit(stmt_comp, setup=setup, number=500)

print(f"for+append: {t_for:.4f}s")
print(f"listcomp : {t_comp:.4f}s")
実行結果
for+append: 0.1200s
listcomp : 0.0950s

目安として、次のように使い分けると良いです。

  • シンプルな変換・フィルタ・最大二重ループまで: リスト内包表記
  • 三重以上のネスト、複雑な条件や多数の分岐、副作用を伴う処理: 通常のfor文に分解
  • メモリ節約が最重要のときはジェネレータ式も検討(本記事では詳細割愛)

下表は状況別の指針です。

状況推奨
単純な写像(map)やフィルタリスト内包表記
二重ループでの平坦化などリスト内包表記(読みやすさを保てる範囲で)
三重以上のネストや複雑分岐通常のfor文に分割
大量データで逐次処理したいジェネレータ式(別記事で解説)

まとめ

本記事では、for文を1行に置き換えるリスト内包表記の基本から応用、そして安全に使うための注意点までを段階的に解説しました。

大切なのは、

  1. フィルタifは末尾、if-elseは左端の式に置くという配置ルール
  2. 読み順を意識して「何を作るか」を先に示すこと
  3. 副作用や過度なネストを避けて可読性を守ること、の3点です。

これらを守れば、コードは簡潔になり、保守性と実行効率の両方を高められます。

今日から小さなループを見つけたら、まずは内包表記に置き換えられるか検討してみてください。

Pythonを扱う求人
読み込み中…
Python 実践TIPS - コーディング効率化・Pythonic

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!