0コメント

東大の教材で学ぶ!Python内包表記の練習問題5選:分散計算から行列加算まで

【Python初心者必見】内包表記の完全ガイド:リスト・辞書・ジェネレータまで網羅的に解説

🐍 【完全網羅】Python内包表記マスターガイド:リストから辞書、ジェネレータ式まで徹底解説

Pythonの「内包表記(Comprehension)」は、リストや辞書などのデータ構造をシンプルに、そして高速に生成するための強力な構文です。本記事では、基本から応用、メモリ効率の最適化まで徹底解説します!


📑

はじめに:内包表記を攻略する「5つの重要用語」

Pythonのコードを劇的に短く、美しくする「内包表記」。しかし、その仕組みを深く理解するには、背後にあるいくつかの専門用語を整理しておく必要があります。 これから解説する記事の基礎知識として、まずは以下の5つのキーワードをチェックしてみましょう。

① イテラブル (Iterable)

リストや文字列など、for文で中身を一つずつ取り出せるオブジェクトのこと。内包表記の「ソース(源)」になります。

② イテレータ (Iterator)

次に渡すべき要素を覚えているオブジェクト。ジェネレータ式によって生成され、必要な時だけ計算を行う省エネ担当です。

③ キャスト (Cast)

int('123')のように、データの型(文字列→数値など)を変換すること。練習問題の数値変換で重要な役割を果たします。

④ ネスト (Nested)

「入れ子」構造のこと。内包表記の中にさらに内包表記を書くことで、二次元の行列などを効率的に処理できます。

⑤ ジェネレータ式

リストを即座に作らず、反復可能なオブジェクトを生成する数式。sum()などの引数に使うとメモリを節約できます。

これらの言葉は、公式ドキュメントやプロの現場でも当たり前に使われます。用語をセットで覚えることで、エラー解決やリサーチのスピードも格段に上がりますよ!

📌 目次

  • 1. リスト内包表記の基本(自乗リスト・文字変換)
  • 2. 実践的な練習問題:文字列操作と統計計算
  • 3. 入れ子(ネスト)の内包表記:行列計算への応用
  • 4. 条件付き内包表記(ifの活用)
  • 5. セット・辞書内包表記
  • 6. メモリを節約する「ジェネレータ式」
  • 7. 具体的な使用シーンとまとめ

1. 📝 リスト内包表記の基本

従来のforループを1行に凝縮できます。プログラムが読みやすくなるのが最大のメリットです。

✅ 基本の自乗リスト

# 通常の書き方 squares1 = [] for x in range(6): squares1.append(x**2) # 内包表記 squares2 = [x**2 for x in range(6)] # 出力: [0, 1, 4, 9, 16, 25]

✅ 文字コードの変換

3-2で登場したアルファベット生成も内包表記ならスマートです。

[chr(i + ord('a')) for i in range(26)] # aからzまでのリスト

2. 🏋️ 練習問題の徹底解説

🔹 文字列の長さをリスト化

strings = ['The', 'quick', 'brown'] に対して各単語の文字数を取得します。

[len(x) for x in strings] # [3, 5, 5]

🔹 カンマ区切り文字列の数値化

splitメソッドとint関数を組み合わせます。

str1 = '123,45,-3' [int(x) for x in str1.split(',')] # [123, 45, -3]

🔹 分散(variance)の計算

sum関数と組み合わせて、統計計算も簡潔に定義可能です。

def var(lst): n = len(lst) av = sum(lst) / n # 偏差の二乗の総和をnで割る return sum([(x - av)**2 for x in lst]) / n

3. 🕸️ 入れ子(ネスト)の内包表記

行列のような多次元構造を扱う際に便利です。外側のループから順に読み解くのがコツです。

✅ 二次元リストの生成

[[x*y for y in range(x+1)] for x in range(4)] # 出力: [[0], [0, 1], [0, 2, 4], [0, 3, 6, 9]]

✅ 行列の加算(sum_matrix)

二つの3x3行列(list1, list2)の各要素を合計します。

def sum_matrix(list1, list2): return [[list1[i][j] + list2[i][j] for j in range(3)] for i in range(3)]

4. ⚠️ 条件付き内包表記(Filtering)

if文を追加することで、特定の要素だけを抽出できます。

words = ['cat', 'dog', 'elephant', None, 'giraffe'] length = [len(w) for w in words if w != None] # Noneを除外!

5. 🗂️ セット・辞書内包表記

リスト以外にも適用可能です。用途に合わせて使い分けましょう。

  • セット内包表記: 重複を自動的に排除します。 {len(w) for w in words}
  • 辞書内包表記: キーと値のペアを生成します。 {w: len(w) for w in words}

6. ⚡ ジェネレータ式:メモリ効率の最適化

カッコを()にすると「ジェネレータ式」になります。これはリストを丸ごと作らず、必要になるたびに値を生成するため、巨大なデータを扱う際のメモリ消費を劇的に抑えられます。

# リストを作らずに直接合計を計算(メモリに優しい) sum(x ** 2 for x in range(1000000))

7. 💡 具体的な使用状況の解説

内包表記はどのような場面で使われるべきでしょうか?

シーン 内包表記のメリット
データクリーニング CSVから読み込んだ文字列を数値に一括変換する際などに重宝します。
APIレスポンスの整形 JSONデータから特定のキー(IDなど)だけを抽出してリスト化するのに最適です。
数学的計算 行列演算やベクトル演算を外部ライブラリを使わず簡潔に書きたい場合に便利です。

⭐ まとめ

  • 簡潔さ: 複数行のループを1行にまとめ、コードの意図を明確にする。
  • 多様性: リスト、セット、辞書、ジェネレータのすべてで利用可能。
  • 効率: 集計処理にはメモリを節約できるジェネレータ式を活用する。
  • 注意点: 複雑すぎる入れ子は逆に可読性を下げるので、適度な使用を心がける。

参考:6-1. 内包表記 — Pythonプログラミング入門 (東京大学)

🔍 練習問題:徹底ステップアップ解説

教材に登場した各練習問題について、なぜそのコードになるのかを一つずつ紐解いていきましょう。

1. 文字列から長さのリストを作成

strings = ['The', 'quick', 'brown'][3, 5, 5] に変換する問題です。

[len(x) for x in strings]

解説: for x in strings でリストから1単語ずつ取り出し、それを len(x) に渡して文字数をカウントしています。データの「前処理」で頻出するパターンです。

2. カンマ区切り文字列の数値リスト化

'123,45,-3' という文字列を、計算可能な数値のリストに変換します。

[int(x) for x in str1.split(',')]

解説: まず str1.split(',')['123', '45', '-3'] という「文字列のリスト」を作ります。その各要素 xint(x) で整数にキャスト(型変換)しています。

3. 統計関数:分散 (var) の実装

数式の $\frac{1}{n} \sum (x_i - \bar{x})^2$ をプログラムに落とし込みます。

def var(lst): av = sum(lst) / len(lst)  # 平均値の計算 return sum([(x - av)**2 for x in lst]) / len(lst)

解説: 内包表記の中で (x - av)**2(偏差の二乗)を計算し、そのリストを sum() で合計しています。数式をほぼそのままコードにできるのが内包表記の強みです。

4. ネストしたリストの総和 (sum_lists)

リストのリスト [[20, 5], [6, 16], ...] の全要素を足し合わせます。

sum([sum(lst) for lst in list1])

解説: 内側の sum(lst) で各サブリストの合計(例:[25, 22, ...])を作り、最後に外側の sum() でそれらをすべて合計します。

5. 二次元リスト(行列)の加算

同じ位置にある要素同士 list1[i][j] + list2[i][j] を足します。

[[list1[i][j] + list2[i][j] for j in range(3)] for i in range(3)]

解説: i が行を、j が列を指します。内側の [... for j in range(3)] が1行分の計算結果を作り、それを外側のループで3行分繰り返して「行列」の形を維持しています。

引用元:6-1. 内包表記 — Pythonプログラミング入門

🚀 さらなる高みへ:内包表記の背景と周辺知識

お疲れ様でした!内包表記をマスターしたあなたは、Pythonic(Pythonらしい)なコードへの大きな一歩を踏み出しました。 最後に、実務で役立つ周辺知識をいくつかご紹介します。これを知っていると、コードの「質」がさらに変わります。

🌟 Pythonの哲学:The Zen of Python

Pythonには "Beautiful is better than ugly."(醜いより美しい方が良い)という哲学があります。内包表記が推奨されるのは、単に短いからではなく、ループの意図が「リストを作ることである」と一目で伝わる宣言的な美しさがあるからです。

⚡ パフォーマンスの真実

実は、内包表記は通常の for 文 + append() よりも実行速度が速い傾向にあります。これは、内包表記がPythonの内部(C言語レベル)で最適化されたループ処理を行うためです。大量のデータを扱う際、この差が大きなパフォーマンス向上に繋がります。

🛡️ 可読性の限界点

「何でも内包表記で書けば良い」わけではありません。条件分岐が複雑になりすぎたり、入れ子が深くなりすぎた場合は、あえて通常の for 文に戻す勇気も必要です。「自分以外の人が3ヶ月後に見て理解できるか」が、良いコードの判断基準になります。

次のステップ:次は「ラムダ式」や「map関数」、あるいは「NumPy」などのライブラリを学習してみましょう。 内包表記との使い分けを知ることで、データ処理の幅が劇的に広がります。

🗂️ 内包表記・暗記必須フラッシュカード(全20問)

カードにマウスを乗せるかタップして、裏面の重要事項を確認しましょう!

リスト内包表記の括弧
角括弧 [ ] を使用する
内包表記の基本構造
[式 for 変数 in イテラブル]
条件付き内包表記
forの後に if 条件文を記述する
ジェネレータ式の括弧
丸括弧 ( ) を使用する
ジェネレータ式の利点
要素を都度生成するためメモリ消費が少ない
辞書内包表記の構造
{キー: 値 for 変数 in イテラブル}
セット内包表記の特徴
重複する要素が自動的に除外される
sum関数との併用
内包表記やジェネレータの全要素を合計する
ネストした内包表記
内包表記の中に内包表記を書く「入れ子」構造
文字列を1文字ずつリスト化
[char for char in "text"]
2重ループ内包表記の順序
左側のforが外側、右側のforが内側のループ
split()メソッドの役割
文字列を特定の区切り文字で分割しリストにする
int()関数の役割
文字列などの型を整数型に変換(キャスト)する
chr()関数の役割
整数を対応するUnicode文字に変換する
ord()関数の役割
1文字の文字列をUnicodeコードポイントに変換する
分散の計算に使う関数
sum(), len() と内包表記を組み合わせて算出
The Zen of Python
美しく簡潔なコードを良しとするPythonの設計哲学
イテラブル (Iterable)
for文で要素を一つずつ取り出し可能なオブジェクト
内包表記の実行速度
通常のappendループより高速に動作することが多い
内包表記を避けるべき時
構造が複雑すぎて可読性が著しく低下する場合

出典:東京大学「Pythonプログラミング入門」6-1. 内包表記を基に作成

⚠️ その知識、正解?ひっかけチェックテスト

クリック(タップ)すると、うっかり間違いやすいポイントと解説が表示されます。

Q1. 辞書内包表記は [ ] で囲む?
❌ 不正解!
辞書内包表記とセット内包表記は { }(波括弧) を使います。 [ ] はリスト専用です。
Q2. (x for x in range(3)) の結果は [0, 1, 2] になる?
❌ 不正解!
これは「ジェネレータ式」です。中身を即座にリストとして見たい場合は list() で囲む必要があります。そのまま出力すると <generator object...> と表示されます。
Q3. 二重ループ [x*y for x in A for y in B] は右側が外側のループ?
❌ 不正解!
左側が「外側」、右側が「内側」のループです。普通のfor文を上から順に書いたときと同じ並び順だと覚えましょう。
Q4. 内包表記の中で else も使える?
⭕ 正解!
[x if x > 0 else 0 for x in list] のように、三項演算子と組み合わせれば else も使えます。ただし、フィルタリングの if(末尾に書くもの)には else は付けられません。
Q5. 内包表記は常に for 文より速い?
🤔 ほぼ正解(注意あり)!
多くの場合、append()を繰り返すより高速ですが、処理があまりに複雑な場合は差がなくなります。「速度よりも可読性」で選ぶのがPython流です。
💡 攻略のヒント:
特に「ジェネレータ式 ( )」と「リスト内包表記 [ ]」の違いは試験や面接でもよく狙われます。丸括弧を見たら「あ、すぐには計算されないやつだ!」と反応できるようにしましょう。

🛠️ 内包表記のトラブルシューティング(Q&A)

「エラーが出る」「意図した結果にならない」時の解決策をまとめました。

Q1. SyntaxError: invalid syntax が出て実行できません。

原因: 括弧の閉じ忘れや、forin の順番間違いがほとんどです。

チェックポイント:
・最後に ] を忘れていませんか?
[x for x in data] の形になっていますか?(in の位置に注意)
・if文を使う場合、[x for x in data if x > 0] のように if は最後にありますか?

Q2. 出力が <generator object...> になって中身が見えません。

原因: 括弧を ( ) で書いていませんか?

解決策: ( ) で囲むと「ジェネレータ式」になり、リストは作成されません。中身を今すぐリストとして確認したい場合は、全体を list( ... ) で囲むか、括弧を [ ] に変更してください。

Q3. NameError: name 'x' is not defined と言われます。

原因: for の後で宣言した変数名と、先頭の「式」で使っている変数名が一致していない可能性があります。

修正例:
[y ** 2 for x in range(5)](yが未定義)
[x ** 2 for x in range(5)](変数名を統一する)

Q4. 二次元リストを作ったつもりが、1次元の長いリストになってしまいます。

原因: 括弧の入れ子構造が不足しています。

解決策:
・フラットなリスト:[x*y for x in A for y in B]
・二次元リスト:[[x*y for y in B] for x in A]
内側のループを [ ] で囲むことで、1行分のリストが要素として格納されるようになります。

困ったときは公式ドキュメントも確認:Python公式チュートリアル

📚 用語事典:内包表記と周辺知識のすべて【完全網羅版】

内包表記の理解に必要な基礎知識から、教材に登場する全ての組み込み関数・概念を網羅しました。

■ 内包表記 (Comprehension)
コレクションを生成するための簡潔な記法。[式 for 変数 in イテラブル if 条件]の形式を基本とし、ループ処理を1行に凝縮します。
■ イテラブル (Iterable)
for文で中身を順番に取り出せるオブジェクト(リスト、文字列、rangeなど)。内包表記のソース(供給源)となります。
■ イテレータ (Iterator)
「次の要素」を順次生成して返すオブジェクト。ジェネレータ式によって構築され、メモリ効率に優れています。
■ 入れ子 / ネスト (Nested)
内包表記の中にさらに内包表記を含める構造。二次元リスト(行列)の生成や、二重ループを1行で記述する際に使用します。
■ 辞書・セット内包表記 (Dictionary / Set Comprehension)
波括弧 {} を用いる記法。辞書型は {k: v} 形式、セット型は {v} 形式で、重複を許さない集合やマップを生成します。
■ ジェネレータ式 (Generator Expression)
丸括弧 () を用いる記法。リストを即座に生成せず、必要に応じて計算を行うため、巨大なデータの集計処理(sum等)に適しています。
■ sum / len 関数
sumは要素の総和、lenは要素数を求めます。内包表記と組み合わせることで、分散や平均の算出を効率化します。
■ range 関数
連続した整数のシーケンスを生成します。内包表記のループ回数を指定する際や、特定の数値リストを生成する起点となります。
■ split メソッド
文字列を指定した区切り文字で分割し、リスト化します。CSV形式のデータ処理において内包表記の前処理として頻出します。
■ chr / ord 関数
数値と文字を相互変換します(例:65 ⇄ 'A')。アルファベット順のリスト作成など、文字データの動的生成に活用されます。
■ キャスト (Type Casting)
int()などを用いてデータ型を変換すること。内包表記内で「文字列リストを整数リストに変える」といった操作を指します。
■ None (NoneType)
「値が存在しないこと」を表す特別な定数。条件付き内包表記において、欠損値を除外(フィルタリング)する際の判定に使われます。
■ 走査 (Traverse)
データ構造のすべての要素を一度ずつ訪問すること。内包表記は、この走査と加工を同時に行う機能と言い換えられます。
■ 分散 (Variance)
平均からの「データの散らばり」を示す統計量。教材では、偏差の二乗を内包表記で算出する例題として扱われています。
■ Pythonic (パイソニック)
Pythonの哲学に合致した、簡潔で読みやすいコードの書き方。内包表記の習得は、Pythonicなプログラマーへの登竜門です。

出典・引用元:東京大学 Pythonプログラミング入門 6-1. 内包表記

この記事へのコメント