Python:内包表記
1学習の目的
- 「空のリストを作って for で append する」3〜4行の処理を、内包表記の1行で書けるようになる。
- 変換・絞り込み・条件分岐を組み合わせたデータ加工を書けるようになる。Pythonらしいコードの見た目はここで決まる。
2基礎解説
| やりたいこと | 書き方 | 意味 |
|---|---|---|
| 全部を変換 | [n * 2 for n in nums] | 各要素を2倍にした新リスト |
| 条件で絞る | [n for n in nums if n > 5] | 条件に合うものだけ集める |
| 変換+絞り込み | [n ** 2 for n in nums if n > 2] | 絞ってから変換 |
| 辞書を作る | {k: v * 2 for k, v in d.items()} | 辞書内包表記 |
- 語順は [作りたいもの for 変数 in データ]。先に結果、後ろに材料。for文を書いてから前に出す、と考えると迷わない。
- 絞り込みの if は for の後ろ:[n for n in nums if n > 0]。
- 変換を分岐させる if は for の前(三項演算子):["合格" if s >= 60 else "不合格" for s in scores]。位置で意味が変わる。
- 内包表記は新しいリストを返す。元のデータは書き換わらない。
- 読みにくくなったら普通の for に戻す。短さより読みやすさが優先。入れ子や複雑な条件を無理に1行にしない。
現場使用例:CSVから特定の列だけ抜き出す、税込価格リストの生成、条件に合う行だけ抽出 [r for r in rows if r["status"] == "有効"]、文字列の前後空白除去。
nums = [1, 2, 3, 4, 5]
doubled = [n * 2 for n in nums]
print(doubled) # [2, 4, 6, 8, 10]
evens = [n for n in nums if n % 2 == 0]
print(evens) # [2, 4]
squares = [n ** 2 for n in nums if n > 2]
print(squares) # [9, 16, 25]
# 辞書内包表記
prices = {"りんご": 150, "みかん": 100}
taxed = {name: int(p * 1.1) for name, p in prices.items()}
print(taxed) # {'りんご': 165, 'みかん': 110}
3基本ドリル(10問)
出力 == [2, 4, 6, 8]
[n * 2 for n in nums] の形。
nums = [1, 2, 3, 4] print([n * 2 for n in nums])
内包表記の最小形。for で append する3行が1行になる。まずこの語順を体に入れる。
A. [0, 1, 2, 3, 4] B. [1, 2, 3, 4, 5] C. [5] D. エラーになる 選択★☆☆無料
解答 == A
range(5) が生成する数を思い出す。
A
range をそのままリストにした形。PY05の「5は含まない」がここでも効いている。
出力 == [8, 10, 6]
[n for n in nums if n > 5] の形。ifはforの後ろ。
nums = [3, 8, 1, 10, 6] print([n for n in nums if n > 5])
絞り込みの形。変換せずそのまま集めるので、先頭は n だけでよい。元の並び順は保たれる。
names = ["田中", "鈴木"] print([____ for n in names])
出力 == ['田中さん', '鈴木さん']
文字列は + でつなげる。
print([n + "さん" for n in names])
角カッコの先頭には「1個ぶんをどう加工するか」を書く。f-string を使って f"{n}さん" と書いてもよい。
A. for の前 B. for の後ろ C. 角カッコの外 D. どちらでもよい 選択★☆☆無料
解答 == B
絞り込みなのか、変換の分岐なのかで位置が変わる。
B
絞り込みの if は後ろ、変換を分岐させる if(三項演算子)は前。この使い分けが内包表記最大のつまずきポイント。
出力 == [4, 16, 36]
[n ** 2 for n in nums if n % 2 == 0]。絞り込みと変換を同時に書ける。
nums = [1, 2, 3, 4, 5, 6] print([n ** 2 for n in nums if n % 2 == 0])
読む順番は「後ろから」。for で回し → if で絞り → 先頭の式で変換、という流れで動く。
scores = [82, 45, 91] print([____ for s in scores])
出力 == ['合格', '不合格', '合格']
三項演算子「値 if 条件 else 値」を for の前に置く。
print(["合格" if s >= 60 else "不合格" for s in scores])
要素数は減らずに中身だけが変わる。D03の絞り込み(要素が減る)との違いを見比べてほしい。
A. [2, 4, 6] B. [1, 2, 3] C. 空リスト D. エラーになる 選択★★☆無料
解答 == B
内包表記は元のリストを書き換えるのか、新しく作るのか。
B
内包表記は新しいリストを返すだけで元データは無傷。だから元の値と加工後の値を両方使える。
出力 == ['田中', '鈴木', '佐藤']
文字列の strip() が前後の空白を取り除く。
names = [" 田中 ", "鈴木 ", " 佐藤"] print([n.strip() for n in names])
strip() は入力データのゆらぎを吸収する定番メソッド。取り込んだデータをまず内包表記で整えるのが実務の第一歩。
出力 == {'りんご': 165, 'みかん': 110}
{name: int(p * 1.1) for name, p in prices.items()} の形。角カッコではなく波カッコ。
prices = {"りんご": 150, "みかん": 100}
print({name: int(p * 1.1) for name, p in prices.items()})
波カッコと キー: 値 にすると辞書になる。PY07の items() がそのまま使える。
4実践シナリオ(5問)
出力3行が完全一致
taxed = [int(p * 1.1) for p in prices] を作ってから、enumerate で番号を使って対応させる。
prices = [1200, 850, 3400]
taxed = [int(p * 1.1) for p in prices]
for i, p in enumerate(prices):
print(f"{p:,}円 → {taxed[i]:,}円")
加工と表示を分ける設計。先に内包表記でデータを作っておくと、表示部分のループが単純になる。
出力2行が完全一致
passed = [s for s in scores if s >= 60]。人数は len()、平均は sum() / len() で {x:.1f}。
scores = [82, 45, 91, 67, 38, 76]
passed = [s for s in scores if s >= 60]
print(f"合格者:{passed}")
print(f"{len(passed)}人 / 平均{sum(passed) / len(passed):.1f}点")
PY05のS03ではループとカウンタで書いた処理が1行+集計関数になった。抽出したリストがあれば件数も平均もすぐ出せる。
出力 == [100, 250, 80] / 合計:430(2行)
int() は前後の空白があっても変換してくれる。[int(r) for r in raw] でよい。
raw = [" 100 ", "250", " 80 "]
nums = [int(r) for r in raw]
print(nums)
print(f"合計:{sum(nums)}")
CSVやフォームから来る値はすべて文字列。int() や float() へ一括変換するのは実務で毎回発生する処理で、内包表記の出番そのもの。
出力5行が完全一致
marks = ["達成" if s >= target else "未達" for s in sales] を作る。達成数は marks.count("達成") で数えられる。
sales = [128000, 95000, 143000, 87000]
target = 120000
marks = ["達成" if s >= target else "未達" for s in sales]
for i, s in enumerate(sales):
print(f"{i + 1}月:{s:,}円 {marks[i]}")
print(f"達成:{marks.count('達成')} / {len(sales)}ヶ月")
三項演算子つきの内包表記で判定結果のリストを先に作っておくと、表示も集計も後から自由にできる。count() で数えられるのが利点。
出力2行が完全一致
{name: num for name, num in stock.items() if num < threshold}。合計は sum(low.values())。
stock = {"ボールペン": 120, "ノート": 8, "ファイル": 3, "付箋": 67}
threshold = 10
low = {name: num for name, num in stock.items() if num < threshold}
print(f"要発注:{low}")
print(f"{len(low)}品目 / 合計{sum(low.values())}個")
辞書内包表記でもif で絞り込める。PY07のS03ではループで書いた在庫チェックが1行になり、しかも結果が辞書として残るので再利用できる。
5仕上げ課題
logs = [
"200 /home", "404 /about", "200 /login",
"500 /api", "200 /home", "404 /contact", "200 /home"
]
各ログは「ステータスコード(3文字)+半角スペース+パス」の形式。
仕様(3つとも内包表記で作ること):
① codes — ステータスコードを整数にしたリスト
② success_paths — コードが200のログのパスだけのリスト
③ errors — コードが400以上のログをそのまま集めたリスト
出力仕様(13行):
=== アクセスログ集計 ===
総リクエスト:7件
成功:4件
エラー:3件
出現コード:[200, 404, 500]
[成功したパス]
/home、/login、/home、/home
[エラー詳細]
404 /about
500 /api
404 /contact
---
成功率:57.1%
出力13行が完全一致
文字列にもスライスが使える。log[:3] で先頭3文字(コード)、log[4:] で4文字目以降(パス)が取れる。出現コードは PY07 の set() と sorted() で重複を除く。パスの連結は "、".join(リスト)。
logs = [
"200 /home", "404 /about", "200 /login",
"500 /api", "200 /home", "404 /contact", "200 /home"
]
codes = [int(log[:3]) for log in logs]
success_paths = [log[4:] for log in logs if log[:3] == "200"]
errors = [log for log in logs if int(log[:3]) >= 400]
print("=== アクセスログ集計 ===")
print(f"総リクエスト:{len(logs)}件")
print(f"成功:{len(success_paths)}件")
print(f"エラー:{len(errors)}件")
print(f"出現コード:{sorted(set(codes))}")
print("[成功したパス]")
print("、".join(success_paths))
print("[エラー詳細]")
for e in errors:
print(e)
print("---")
print(f"成功率:{len(success_paths) / len(logs) * 100:.1f}%")
生データ → 内包表記で3本の加工済みリスト → あとは表示するだけという流れが要点。加工と表示を分けておくと、集計項目が増えても内包表記を1行足すだけで対応できる。文字列スライス(PY06)、set(PY07)、f-string(PY03)が全部つながる総合問題で、ここまで書ければログ解析の実務スクリプトが書ける。