Q1次のコードを実行したときの出力はどれですか?nums = [1, 2, 3, 4]
result = [n * 10 for n in nums]
print(result)
リスト内包表記と辞書・集合・ジェネレーター式 — 1 行でコレクションを作る
Python のリスト内包表記を基礎から解説します。for を 1 行に圧縮する書き方から辞書・集合・ジェネレーター式まで図解で押さえます。
前回の all() / any() ではコレクションをまとめて判定する方法を見ました。今回は、コレクションそのものを 1 行で作るための強力な構文、内包表記(comprehension)を整理します。
リスト内包表記を中心に、if でフィルタする書き方、辞書・集合を作るバリエーション、そしてメモリを節約するジェネレーター式まで扱います。
リスト内包表記の基本 — for を 1 行に圧縮
Python では、リストを 1 行で作る [式 for 変数 in 反復可能オブジェクト] という書き方があります。これをリスト内包表記と呼びます。
空リストに append() を繰り返す for ループの短縮形で、リスト全要素に同じ変換をかけて新しいリストを作りたい場面で重宝します。
[式 for 変数 in 反復可能] は、各要素を式で変換した新しいリストを作ります。for ループで append する書き方の 1 行版です。
# for ループでの書き方
result = []
for price in [100, 250, 480]:
result.append(int(price * 1.1))
print(result) # [110, 275, 528]
# 同じ処理をリスト内包表記で
prices = [100, 250, 480]
tax_included = [int(p * 1.1) for p in prices]
print(tax_included) # [110, 275, 528]
# 文字列の長さを集めるのも 1 行
names = ["Alice", "Bob", "Charlotte"]
lengths = [len(n) for n in names]
print(lengths) # [5, 3, 9]
if でフィルタする条件付き内包表記
for の後ろに if 条件 を加えると、条件を満たす要素だけを新しいリストに残せます。[式 for 変数 in 反復可能 if 条件] の形です。
変換とフィルタが 1 行で書けるのがリスト内包表記の強みで、「特定の条件を満たすデータだけを取り出す」処理を簡潔に表現できます。
if p >= 200 で 1 件ずつ判定する。True の要素(採用)だけが新しいリストに残り、False の要素(除外)は捨てられる。# 200 円以上だけ抜き出す
prices = [80, 250, 120, 480, 95]
premium = [p for p in prices if p >= 200]
print(premium) # [250, 480]
# フィルタ + 変換を同時に
discounted = [int(p * 0.9) for p in prices if p >= 200]
print(discounted) # [225, 432] ← 200 円以上だけ 10% オフ
# 文字列の絞り込み + 変換
names = ["Alice", "Bob", "Charlotte", "Ed"]
long_names = [n.upper() for n in names if len(n) >= 5]
print(long_names) # ['ALICE', 'CHARLOTTE']
辞書内包表記と集合内包表記
リスト内包表記の角括弧 [ ] を波括弧 { } に変えると集合(set)内包表記になります。
さらに {キー: 値 for ...} のように : を挟むと辞書(dict)内包表記です。for ... in ... の文法はそのままに、出力するコレクションの形だけを切り替えられるのが内包表記の便利なところです。
{キー: 値 for ...} の形でペアの辞書を作れます。商品名と価格のリストから「名前 → 価格」の lookup を 1 行で構築できます。
# 辞書内包表記: 商品名 → 価格 の lookup を作る
pairs = [("apple", 120), ("orange", 80), ("banana", 60)]
price_lookup = {name: price for name, price in pairs}
print(price_lookup)
# {'apple': 120, 'orange': 80, 'banana': 60}
# 既存の dict から「税込価格」の dict を作り直す
tax_included = {name: int(price * 1.1) for name, price in pairs}
print(tax_included)
# {'apple': 132, 'orange': 88, 'banana': 66}
# 集合内包表記: 重複なしのユニークなタグを取り出す
tags = ["sale", "new", "sale", "limited", "new"]
unique = {t for t in tags}
print(unique) # {'sale', 'new', 'limited'}(順序は実行時依存)
辞書の組み立てに zip() も合わせて使える
2 つのリスト names と prices を組み合わせて辞書を作るときは、{n: p for n, p in zip(names, prices)} のように zip() と一緒に使うと簡潔です。zip() で複数のリストを並走させながら、辞書のキーと値を同時に組み立てられます。
ジェネレーター式 — 角括弧を丸括弧に
リスト内包表記の角括弧 [ ] を丸括弧 ( ) に変えると、ジェネレーター式になります。リストのように全件をメモリに作らず、必要になった分だけ計算する遅延評価の仕組みです。
sum() / max() / min() / any() などにそのまま渡せて、巨大なデータの集計でメモリ消費を抑えられます。詳細は後のジェネレーター関数の記事で扱います。
| 構文 | 結果の型 | 特徴 |
|---|---|---|
| [x for x in items] | list | 全件をメモリに保持 |
| {x for x in items} | set | 重複なし・全件保持 |
| {k: v for k, v in items} | dict | キー: 値の対応・全件保持 |
| (x for x in items) | generator | 現在の 1 件のみ・遅延評価 |
# 丸括弧でジェネレーター式
prices = [100, 250, 480, 1200]
gen = (int(p * 1.1) for p in prices)
print(type(gen)) # <class 'generator'>
# sum() に直接渡せる(外側の () は省略可)
total_tax = sum(int(p * 1.1) for p in prices)
print(total_tax) # 2233
# max() / min() でも同様
max_tax = max(int(p * 1.1) for p in prices)
print(max_tax) # 1320
「全件まとめて使う?1 件ずつ流す?」で選ぶ
全件をリストとして手元に置きたいなら [ ](リスト内包表記)。sum() / max() などに渡して集計するだけなら ( )(ジェネレーター式)の方がメモリに優しい選択です。100 万件規模だと差が顕著で、ジェネレーター式は数百バイトしか使いません。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2次のコードを実行したときの出力はどれですか?nums = [1, 2, 3, 4, 5]
result = [n for n in nums if n % 2 == 0]
print(result)
Q3次のうち辞書を作るのはどれですか?