Pythonの内包表記とは
内包表記(comprehension)は、リストや辞書・集合などのコレクションを、簡潔に生成するための記法です。
通常のforループとappendを組み合わせた書き方を、1行にまとめることができます。コードの見通しがよくなるだけでなく、処理速度の面でも有利になるケースがあります。
この記事では、以下の内容を解説します。
- リスト内包表記の基本的な書き方とforループとの比較
- 条件付き(if)や if-else での絞り込みと変換
- 二重ループのネスト
- 辞書内包表記・集合内包表記
- forループとの速度差の目安
- 可読性の境界と使いどころ
基本の書き方:forループと内包表記の比較
まず、通常のforループで書いたコードを見てみます。
# forループ版
squares = []
for x in range(5):
squares.append(x ** 2)
print(squares)
[0, 1, 4, 9, 16]
同じ処理を内包表記で書くと、次のようになります。
# 内包表記版
squares = [x ** 2 for x in range(5)]
print(squares)
[0, 1, 4, 9, 16]
構造は [式 for 変数 in イテラブル] です。forループ版と同じ結果を1行で書けます。
条件付きの内包表記(if)
末尾に if を付けると、条件に合う要素だけを絞り込めます。
# 偶数だけを取り出す
evens = [x for x in range(10) if x % 2 == 0]
print(evens)
[0, 2, 4, 6, 8]
for の後ろに if を置くのがポイントです。この if はフィルタとして機能し、条件を満たす要素だけがリストに入ります。
if-elseを使った変換(条件に応じて値を切り替える)
絞り込みではなく「条件によって値を変える」場合は、if-else を式の先頭側に置きます。
# 偶数なら"even"、奇数なら"odd"に変換
labels = ["even" if x % 2 == 0 else "odd" for x in range(6)]
print(labels)
['even', 'odd', 'even', 'odd', 'even', 'odd']
構造は [真の値 if 条件 else 偽の値 for 変数 in イテラブル] です。
末尾の if(フィルタ)と先頭の if-else(変換)は役割が異なります。混同しやすい部分なので整理しておきます。
| 書き方 | 役割 | 例 |
|---|---|---|
[x for x in L if 条件] | 絞り込み(フィルタ) | 偶数だけ取り出す |
[A if 条件 else B for x in L] | 変換(全要素を処理) | 偶数→A、奇数→B |
複数条件を組み合わせる
if に複数の条件を and や or でつなぐことができます。
# 3の倍数かつ5の倍数
fizzbuzz = [x for x in range(1, 31) if x % 3 == 0 and x % 5 == 0]
print(fizzbuzz)
[15, 30]
条件が複雑になる場合は、可読性のために関数に分けることも検討してください。
タプルを使った内包表記
内包表記の中でタプルを生成することもできます。
# (元の数, 2乗) のタプルのリスト
pairs = [(x, x ** 2) for x in range(5)]
print(pairs)
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]
式の部分にタプルを書く場合、括弧が必要です。括弧を外すと構文エラーになります。
# NG: 括弧なしはエラー
# pairs = [x, x ** 2 for x in range(5)] # SyntaxError
二重ループの内包表記(ネスト)
forループを2つネストした処理も、内包表記で書けます。
# 九九の一部(2〜4の段)
table = [i * j for i in range(2, 5) for j in range(1, 4)]
print(table)
[2, 4, 6, 3, 6, 9, 4, 8, 12]
構造は [式 for 変数1 in イテラブル1 for 変数2 in イテラブル2] です。左から順にループが展開されます。
外側のループが先に来る点は、forループを2段階ネストしたときと同じ順序です。
# 上のコードと等価なforループ
table = []
for i in range(2, 5):
for j in range(1, 4):
table.append(i * j)
ネストが深くなると読みにくくなるため、3段階以上のネストは通常のforループで書く方が無難です。
辞書内包表記・集合内包表記
リスト以外にも、辞書(dict)や集合(set)を内包表記で生成できます。
辞書内包表記
# 文字列の長さを辞書にまとめる
words = ["apple", "banana", "cherry"]
length_dict = {w: len(w) for w in words}
print(length_dict)
{'apple': 5, 'banana': 6, 'cherry': 6}
{キー: 値 for 変数 in イテラブル} の形式です。
集合内包表記
# 重複を除いた集合を生成
nums = [1, 2, 2, 3, 3, 3, 4]
unique = {x for x in nums}
print(unique)
{1, 2, 3, 4}
波括弧を使い、{式 for 変数 in イテラブル} の形式です。辞書内包表記との違いは、コロン(:)がない点です。
forループとの速度比較
内包表記はforループ+appendよりも処理速度が速い傾向があります。
理由は、内包表記がPythonの内部で最適化されているためです。具体的には、ループのたびに append メソッドを呼び出すオーバーヘッドがなく、リストの生成処理が効率よく行われます。
簡単な比較例を示します。
import time
n = 1_000_000
# forループ版
start = time.time()
result = []
for x in range(n):
result.append(x ** 2)
print(f"forループ: {time.time() - start:.4f}秒")
# 内包表記版
start = time.time()
result = [x ** 2 for x in range(n)]
print(f"内包表記: {time.time() - start:.4f}秒")
実行環境によって数値は変わりますが、内包表記の方が速くなるケースが多いです。おおむね以下のような傾向が確認できます。
forループ: 0.1234秒
内包表記: 0.0678秒
大量データのメモリに注意
要素数が多い場合、リストをすべてメモリに展開するのではなく、ジェネレータ式を使う方がメモリ効率がよいです。
# ジェネレータ式(角括弧ではなく丸括弧)
gen = (x ** 2 for x in range(1_000_000))
# 必要な時だけ1要素ずつ取り出す
print(next(gen))
print(next(gen))
0
1
全要素を一度に使わない場合は、ジェネレータ式の方がメモリ消費を抑えられます。
よくあるミスと注意点
変数名の衝突
内包表記の中で使う変数名は、外側のスコープには影響しません(Python 3.x 系)。
x = 100
result = [x for x in range(5)] # 内包表記内のxは別スコープ
print(x) # 外側のxは変わらない
100
Python 2では内包表記の変数がスコープ外に漏れる挙動がありましたが、現在の Python 3.x 系では問題ありません。
if-elseの位置を間違える
# NG: elseがない状態でifを先頭に置く → SyntaxError
# result = [x if x % 2 == 0 for x in range(5)]
# OK: elseとセットで先頭に置く
result = [x if x % 2 == 0 else -1 for x in range(5)]
print(result)
[0, -1, 2, -1, 4]
if を先頭(式の側)に置く場合は、必ず else とセットにする必要があります。
可読性を損なう書き方
内包表記はシンプルな処理を短く書くのに向いています。複雑な処理を1行に詰め込むと、かえって読みにくくなります。
# 読みにくい例(無理に1行に収めた場合)
# result = [f(x) for x in [g(y) for y in range(10)] if h(x)]
# 分けた方が読みやすい
inner = [g(y) for y in range(10)]
result = [f(x) for x in inner if h(x)]
ネストが深い、条件が複雑、処理が複数ステップにわたる場合は、通常のforループに戻す判断も重要です。
AI時代での補足
AIがPythonコードを生成する場面では、内包表記が使われることが増えています。出力を読んで「これは何をしているコードか」と判断できるかどうかは、内包表記の構造を理解しているかどうかにかかっています。forループとの対応を把握しておくと、AIの出力を自分の用途に合わせて修正しやすくなります。
まとめ
この記事では、Pythonの内包表記について以下の内容を解説しました。
[式 for 変数 in イテラブル]が基本形- 末尾の
ifはフィルタ、先頭のif-elseは変換 - 二重ループは
forを並べて書く - 辞書内包表記は
{キー: 値 for ...}、集合内包表記は{式 for ...} - forループより速い傾向があるが、複雑な処理には向かない
- 大量データはジェネレータ式でメモリを節約できる
Pythonの if in 構文やメンバーシップテストについては、Python「if in」を徹底解説:効率的な条件分岐とメンバーシップテストの完全ガイド で詳しく解説しています。あわせて参照してください。
基礎をどこまで自分で習得すべきか迷っている場合は、AIが普及した時代にプログラミングスクールは必要か?【判断基準を解説】 も参考になります。