Python / LESSON 09 / PY09

Python:内包表記

1学習の目的

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()}辞書内包表記
✅ 覚えるべき重要ポイント
  1. 語順は [作りたいもの for 変数 in データ]先に結果、後ろに材料。for文を書いてから前に出す、と考えると迷わない。
  2. 絞り込みの if は for の後ろ[n for n in nums if n > 0]
  3. 変換を分岐させる if は for の前(三項演算子):["合格" if s >= 60 else "不合格" for s in scores]。位置で意味が変わる。
  4. 内包表記は新しいリストを返す。元のデータは書き換わらない。
  5. 読みにくくなったら普通の 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問)

PY09-D01 nums = [1, 2, 3, 4] の各要素を2倍にしたリストを、内包表記で作って出力せよ。 コーディング★☆☆無料
期待される結果

出力 == [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行になる。まずこの語順を体に入れる。

PY09-D02 [n for n in range(5)] の結果はどれか。
A. [0, 1, 2, 3, 4] B. [1, 2, 3, 4, 5] C. [5] D. エラーになる
選択★☆☆無料
期待される結果

解答 == A

ヒント

range(5) が生成する数を思い出す。

模範解答
A
解説

range をそのままリストにした形。PY05の「5は含まない」がここでも効いている。

PY09-D03 nums = [3, 8, 1, 10, 6] から 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 だけでよい。元の並び順は保たれる。

PY09-D04 names の各要素の末尾に「さん」を付けたリストを作るコードを完成させよ。 穴埋め★☆☆無料
コード
names = ["田中", "鈴木"]
print([____ for n in names])
期待される結果

出力 == ['田中さん', '鈴木さん']

ヒント

文字列は + でつなげる。

模範解答
print([n + "さん" for n in names])
解説

角カッコの先頭には「1個ぶんをどう加工するか」を書く。f-string を使って f"{n}さん" と書いてもよい。

PY09-D05 「偶数だけを取り出す」内包表記を書くとき、if を置く位置はどこか。
A. for の前 B. for の後ろ C. 角カッコの外 D. どちらでもよい
選択★☆☆無料
期待される結果

解答 == B

ヒント

絞り込みなのか、変換の分岐なのかで位置が変わる。

模範解答
B
解説

絞り込みの if は後ろ変換を分岐させる if(三項演算子)は前。この使い分けが内包表記最大のつまずきポイント。

PY09-D06 nums = [1, 2, 3, 4, 5, 6] から偶数だけを取り出し、それぞれを2乗したリストを内包表記で出力せよ。 コーディング★★☆無料
期待される結果

出力 == [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 で絞り → 先頭の式で変換、という流れで動く。

PY09-D07 点数のリストを「合格」「不合格」の文字列リストに変換するコードを完成させよ(60点以上が合格)。 穴埋め★★☆無料
コード
scores = [82, 45, 91]
print([____ for s in scores])
期待される結果

出力 == ['合格', '不合格', '合格']

ヒント

三項演算子「値 if 条件 else 値」を for の前に置く。

模範解答
print(["合格" if s >= 60 else "不合格" for s in scores])
解説

要素数は減らずに中身だけが変わる。D03の絞り込み(要素が減る)との違いを見比べてほしい。

PY09-D08 nums = [1, 2, 3] に対して doubled = [n * 2 for n in nums] を実行した後、nums の中身はどうなるか。
A. [2, 4, 6] B. [1, 2, 3] C. 空リスト D. エラーになる
選択★★☆無料
期待される結果

解答 == B

ヒント

内包表記は元のリストを書き換えるのか、新しく作るのか。

模範解答
B
解説

内包表記は新しいリストを返すだけで元データは無傷。だから元の値と加工後の値を両方使える。

PY09-D09 names = [" 田中 ", "鈴木 ", " 佐藤"] の各要素から前後の空白を除いたリストを、内包表記で出力せよ。 コーディング★★☆無料
期待される結果

出力 == ['田中', '鈴木', '佐藤']

ヒント

文字列の strip() が前後の空白を取り除く。

模範解答
names = [" 田中 ", "鈴木 ", " 佐藤"]
print([n.strip() for n in names])
解説

strip() は入力データのゆらぎを吸収する定番メソッド。取り込んだデータをまず内包表記で整えるのが実務の第一歩。

PY09-D10 prices = {"りんご": 150, "みかん": 100} の各値を税込(1.1倍・整数に切り捨て)にした辞書を、辞書内包表記で出力せよ。 コーディング★★☆無料
期待される結果

出力 == {'りんご': 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問)

PY09-S01 【税込価格リスト】prices = [1200, 850, 3400] から税込価格(10%・整数に切り捨て)のリストを内包表記で作り、「1,200円 → 1,320円」の形式で3行出力せよ。 コーディング★★☆無料
期待される結果

出力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]:,}円")
解説

加工と表示を分ける設計。先に内包表記でデータを作っておくと、表示部分のループが単純になる。

PY09-S02 【合格者の抽出】scores = [82, 45, 91, 67, 38, 76] から60点以上を内包表記で抽出し、「合格者:[82, 91, 67, 76]」「4人 / 平均79.0点」の2行を出力せよ。 コーディング★★☆無料
期待される結果

出力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行+集計関数になった。抽出したリストがあれば件数も平均もすぐ出せる。

PY09-S03 【データのクリーニング】文字列で届いた raw = [" 100 ", "250", " 80 "] を整数のリストに変換し、リスト全体と合計を2行で出力せよ。 コーディング★★☆無料
期待される結果

出力 == [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() へ一括変換するのは実務で毎回発生する処理で、内包表記の出番そのもの。

PY09-S04 【売上の達成判定】sales = [128000, 95000, 143000, 87000]、target = 120000。内包表記で「達成」「未達」のリストを作り、「1月:128,000円 達成」の形式で4行出力し、最後に「達成:2 / 4ヶ月」と出力せよ。 コーディング★★★無料
期待される結果

出力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() で数えられるのが利点。

PY09-S05 【要発注リストの抽出】stock = {"ボールペン": 120, "ノート": 8, "ファイル": 3, "付箋": 67}、threshold = 10。辞書内包表記で在庫が threshold 未満の商品だけの辞書を作り、「要発注:{'ノート': 8, 'ファイル': 3}」「2品目 / 合計11個」の2行を出力せよ。 コーディング★★★無料
期待される結果

出力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仕上げ課題

PY09-FINAL 【アクセスログ集計】ログの文字列リストから、内包表記を使って集計レポートを出力せよ。
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)が全部つながる総合問題で、ここまで書ければログ解析の実務スクリプトが書ける。