本文へスキップ
スキルアップカレッジ

データ構造——リスト・タプル・辞書・集合と内包表記

レッスン4:データ構造——リストタプル辞書集合内包表記

このレッスンで学ぶこと

  • リスト(list)の発想と基本操作(append・追加・削除・スライス)を身につける
  • タプル(tuple)と「変更できないデータ」の使いどころを理解する
  • 辞書(dict)の発想(キーと値の対応)を業務データに当てはめる
  • 集合(set)の発想と和・差・積の演算を理解する
  • リスト内包表記で短く読みやすく書く
  • ネストしたデータ構造(辞書のリスト、リストの辞書)を扱える
  • 業務でのデータ構造の使い分けの目安を持つ

前回のレッスンでは、条件分岐と繰り返しを扱いました。本レッスンは、業務で扱うデータを Python の中に表現する方法、つまり「データ構造」を扱います。Python には主要なデータ構造が 4 種類あり、それぞれに得意な場面が違います。「Excel の表をどう表現するか」「重複しないリストをどう扱うか」「キーと値で名前を引きたい」——業務での疑問に、データ構造の選択で答えられるようになります。

リスト(list)——順序付きの集まり

リストは、もっともよく使うデータ構造です。複数の値を順番に並べた集まりを表現します。Excel の 1 列のデータに近い発想です。

リストの作り方

リストは [ ] で囲み、値をカンマで区切って書きます。

prices = [1000, 2500, 800, 4500]
names = ["山田", "鈴木", "佐藤"]
mixed = [1, "two", 3.0, True]   # 異なる型も混在できる
empty = []                       # 空のリスト

要素の取り出し(インデックス

リストの要素は、[インデックス] で取り出せます。インデックスは 0 から始まります。

prices = [1000, 2500, 800, 4500]
print(prices[0])    # 1000(1 番目)
print(prices[1])    # 2500(2 番目)
print(prices[-1])   # 4500(最後)
print(prices[-2])   # 800(後ろから 2 番目)

マイナスのインデックスを使うと、末尾から逆向きに取り出せます。これは Python の便利な特徴です。

スライス——範囲を切り出す

リストでも文字列と同じく、スライスで範囲を切り出せます。

prices = [1000, 2500, 800, 4500, 1200]
print(prices[0:3])  # [1000, 2500, 800](0 番目から 3 番目の手前まで)
print(prices[2:])   # [800, 4500, 1200](2 番目から末尾まで)
print(prices[:2])   # [1000, 2500](先頭から 2 番目の手前まで)

要素の追加・削除

fruits = ["りんご", "みかん"]

# 追加
fruits.append("バナナ")           # 末尾に追加
print(fruits)                      # ['りんご', 'みかん', 'バナナ']

fruits.insert(0, "ぶどう")        # 指定位置に挿入
print(fruits)                      # ['ぶどう', 'りんご', 'みかん', 'バナナ']

# 削除
fruits.remove("みかん")            # 値を指定して削除
print(fruits)                      # ['ぶどう', 'りんご', 'バナナ']

removed = fruits.pop()             # 末尾を取り出して削除
print(removed)                     # バナナ
print(fruits)                      # ['ぶどう', 'りんご']

よく使う操作

操作 内容
len(lst) 要素数を数える
sum(lst) 数値リストの合計
max(lst) / min(lst) 最大・最小
sorted(lst) 並べ替えた新しいリストを返す
lst.sort() リスト自体を並べ替える
lst.reverse() リスト自体を逆順にする
x in lst 含まれるかを判定(True/False)
prices = [1000, 2500, 800, 4500]
print(len(prices))           # 4
print(sum(prices))           # 8800
print(max(prices))           # 4500
print(sorted(prices))        # [800, 1000, 2500, 4500]
print(2500 in prices)        # True

💡 ポイント リストは順序付きで値を持つ、もっとも基本的なデータ構造です。[ ] で書き、[インデックス] で取り出し、appendinsertremovepop で変更でき、lensumsortedin で使いやすく操作できます。

タプル(tuple)——変更できない集まり

タプルはリストとよく似ていますが、「中身を変更できない」点が違います。( ) で囲んで書きます。

point = (10, 20)       # 2 次元の座標
rgb = (255, 100, 50)   # RGB の色
empty = ()             # 空のタプル
single = (5,)          # 要素 1 つだけのタプルはカンマが必要

タプルの読み取り

リストと同じく、インデックスで取り出せます。

point = (10, 20)
print(point[0])  # 10
print(point[1])  # 20

変更できないことの意味

point = (10, 20)
point[0] = 30   # TypeError:タプルの要素は変更できない

「変更できない」のは欠点に見えますが、業務では「不変であるべき値」を表すのに便利です。

  • 座標、RGB の色、緯度経度のような「セットになった値」
  • 関数戻り値で「複数の値を一度に返す」とき(次のレッスン 5 で扱う)
  • 辞書のキーとして使いたい場合(後述)

リストとタプルの使い分け

場面 適切な選択
数を変えたい・編集したい集まり リスト
セットになった不変の値(座標・RGB など) タプル
関数から複数の値を返す タプル
速度・メモリを少しでも節約したい タプル

💡 ポイント タプルは「変更できないリスト」です。( ) で書き、セットになった不変の値や、関数の複数戻り値に向きます。リストと使い分ける発想を持っておきます。

辞書(dict)——キーと値の対応

辞書は「キーと値の組」を集めたデータ構造です。Excel の「ID と名前の対応表」のような感覚で使えます。Python での名称は dict

辞書の作り方

辞書は { } で囲み、キー: 値 の組をカンマで区切って書きます。

person = {
    "name": "山田太郎",
    "age": 30,
    "email": "yamada@example.com",
}

値の取り出し

[キー] で値を取り出せます。

person = {"name": "山田太郎", "age": 30}
print(person["name"])  # 山田太郎
print(person["age"])   # 30

存在しないキーを指定するとエラー(KeyError)になります。

安全に取り出す——.get()

エラーを避けたいときは、.get() メソッドを使います。存在しないキーには None を返し、第 2 引数でデフォルト値を指定できます。

person = {"name": "山田太郎"}
print(person.get("age"))           # None
print(person.get("age", 0))        # 0(age がないので 0)

値の追加・更新・削除

person = {"name": "山田太郎", "age": 30}
person["email"] = "yamada@example.com"   # 追加
person["age"] = 31                         # 更新
del person["email"]                        # 削除
print(person)                              # {'name': '山田太郎', 'age': 31}

辞書を for で回す

person = {"name": "山田太郎", "age": 30, "email": "yamada@example.com"}

# キーだけ
for key in person:
    print(key)

# キーと値の両方
for key, value in person.items():
    print(f"{key}: {value}")

# 値だけ
for value in person.values():
    print(value)

.items().keys().values() の使い分けで、必要なものだけを取り出せます。

辞書の業務での使いどころ

場面
1 件のデータの属性 顧客 1 人、注文 1 件、商品 1 つ
ID と名前の対応 社員番号 → 名前
集計結果 商品ごとの売上合計
設定値の保管 設定キー → 値

💡 ポイント 辞書はキーと値の対応を表すデータ構造で、Excel の対応表や設定値の保管に最適です。[キー] または .get(キー) で取り出し、.items() などで for ループに回せます。

集合(set)——重複しない集まり

集合は「重複しない値の集まり」を扱うデータ構造です。順序は持ちません。{ } で囲んで書きますが、辞書と区別するためにキーがないことに注意します。

集合の作り方

fruits = {"りんご", "みかん", "バナナ", "りんご"}  # 重複は自動で 1 つに
print(fruits)  # {'みかん', 'バナナ', 'りんご'}(順序は不定)

empty = set()  # 空の集合は set() で({} は空の辞書になる)

集合の演算

集合は「数学の集合」と同じ演算ができます。

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a | b)   # 和集合:{1, 2, 3, 4, 5, 6}
print(a & b)   # 積集合:{3, 4}
print(a - b)   # 差集合:{1, 2}
print(a ^ b)   # 対称差:{1, 2, 5, 6}

集合の業務での使いどころ

場面
重複削除 リストから重複を除く
共通の要素を見つける 顧客 A と顧客 B が共に購入した商品
差を見つける 先月にあって今月にないもの
含まれているかの高速判定 value in big_setvalue in big_list より速い
# 重複削除
data = ["山田", "鈴木", "山田", "佐藤", "鈴木"]
unique = list(set(data))
print(unique)  # ['山田', '鈴木', '佐藤'](順不同)

💡 ポイント 集合は「重複しない値の集まり」を扱い、和・差・積などの集合演算ができます。重複削除、共通要素の検出、含まれるかの高速判定によく使います。

リスト内包表記——短く読みやすく書く

Python ならではの便利な書き方が「リスト内包表記」です。for ループでリストを作る処理を、1 行で書けます。

基本形

# for ループで書く場合
squares = []
for i in range(5):
    squares.append(i * i)
print(squares)  # [0, 1, 4, 9, 16]

# リスト内包表記で書く場合
squares = [i * i for i in range(5)]
print(squares)  # [0, 1, 4, 9, 16]

[式 for 変数 in 集まり] という形で、1 行に収まります。

条件付き内包表記

if 条件も組み合わせられます。

# 偶数だけを取り出して 2 乗する
result = [i * i for i in range(10) if i % 2 == 0]
print(result)  # [0, 4, 16, 36, 64]

業務での例

# 価格に税込み計算をする
prices = [1000, 2500, 800, 4500]
with_tax = [int(p * 1.1) for p in prices]
print(with_tax)  # [1100, 2750, 880, 4950]

# 文字列を整える
names = [" 山田 ", "鈴木", " 佐藤 "]
cleaned = [name.strip() for name in names]
print(cleaned)  # ['山田', '鈴木', '佐藤']

リスト内包表記は「短くて読みやすい」場合に使い、複雑になりすぎたら通常の for ループに戻すのが目安です。「1 行で書けるが、読みにくい」なら 2〜3 行の for の方がよい、というのが PEP 8 の精神です。

💡 ポイント リスト内包表記は [式 for 変数 in 集まり] の形で、for ループでリストを作る処理を 1 行で書ける Python ならではの記法。条件付きで if も組み合わせられます。「短くて読みやすい」場合に使い、複雑になったら通常の for ループに戻します。

ネストしたデータ構造

業務のデータは、しばしば「辞書のリスト」「リストの辞書」のように入れ子になります。これを「ネストしたデータ構造」と呼びます。

辞書のリスト——複数件のレコード

students = [
    {"name": "山田", "score": 85},
    {"name": "鈴木", "score": 72},
    {"name": "佐藤", "score": 91},
]

for student in students:
    print(f"{student['name']}:{student['score']}")

CSV ファイルや JSON ファイルから読み込んだデータは、しばしばこの形になります。

リストの辞書——カテゴリごとの集まり

sales_by_region = {
    "東京": [1000, 1500, 1200],
    "大阪": [800, 900, 700],
    "名古屋": [600, 750, 850],
}

for region, sales in sales_by_region.items():
    print(f"{region}:合計 {sum(sales)} 円")

データ構造の選び方の判断フロー

flowchart TD
    A["どんなデータを扱う?"] --> B{順序が大事?}
    B -->|Yes| C{変更する?}
    B -->|No| D{重複を許す?}
    C -->|Yes| E[list を使う]
    C -->|No| F[tuple を使う]
    D -->|Yes・キーで引きたい| G[dict を使う]
    D -->|No・重複なしのみ| H[set を使う]

判断のポイントは 3 つです。

  1. 順序が大事か:大事なら list / tuple、不要なら dict / set
  2. 変更するか:変更するなら list / dict / set、しないなら tuple
  3. キーで引きたいか:引きたいなら dict、重複なしの集まりだけなら set

💡 ポイント 業務データは「辞書のリスト」「リストの辞書」など入れ子になることが多くあります。データ構造の選び方は「順序が大事か」「変更するか」「キーで引きたいか」の 3 問で判断します。

講師の現場メモ:「Excel の 1 シートを、辞書のリストに置き換えた瞬間に世界が広がった」

私(平野)が、独立して 1 年目に、地方の社員食堂運営会社(社員 40 名程度)の発注スクリプトを支援したときの話です。先方の担当者の方は、毎日の食材発注を Excel で管理していました。1 行が 1 食材、列に「商品名」「単価」「単位」「発注先」「最低発注量」「在庫」が並ぶ、おなじみの構造です。

最初の打ち合わせで、担当者の方は「在庫が最低発注量を下回った食材だけを、発注先ごとにまとめて発注書にしたい」と希望されました。Excel でやるならフィルタとピボットを駆使する処理です。私は Python で書きたいと提案し、Excel から CSV 形式でエクスポートしてもらい、Python に読み込みました(CSV は次のレッスン 6 で扱います)。

Python に読み込んだデータは、自然に「辞書のリスト」の形になりました。

items = [
    {"name": "鶏もも肉", "unit_price": 800, "unit": "kg", "supplier": "肉のA商店", "min_order": 5, "stock": 3},
    {"name": "じゃがいも", "unit_price": 200, "unit": "kg", "supplier": "野菜のB商店", "min_order": 10, "stock": 12},
    {"name": "豆腐", "unit_price": 150, "unit": "丁", "supplier": "豆腐のC屋", "min_order": 20, "stock": 8},
    # ……(合計 80 品目)
]

各食材が 1 つの辞書で表現されていることに、担当者の方は最初驚かれていました。「Excel の 1 行が、Python ではこんな形になるんですね」「キーで欲しい情報がすぐ取れるのは便利」と。

次に、在庫が最低発注量を下回った食材だけを取り出すリスト内包表記を、私は書きました。

need_order = [item for item in items if item["stock"] < item["min_order"]]

これで、80 品目の中から不足品目だけが抽出できます。1 行で書けるのが、リスト内包表記の威力です。

最後に、不足品目を発注先ごとにまとめるために、リストの辞書を作りました。

by_supplier = {}
for item in need_order:
    supplier = item["supplier"]
    if supplier not in by_supplier:
        by_supplier[supplier] = []
    by_supplier[supplier].append(item)

これで、by_supplier["肉のA商店"] で肉のA商店に発注すべき食材リストが取れます。発注書のドラフトをこのデータから組み立て、メール文面のテンプレートに当てはめれば、半自動化が完成です。

このシステムの完成後、担当者の方は「Excel の世界では『1 行 1 食材』だったが、Python では『1 食材 1 オブジェクト』として扱えるのが衝撃だった」と語ってくれました。「これで複数の表(仕入先一覧、メニュー、献立予定)を組み合わせる発想ができるようになる」と、次の改善案を自分で考えるようになったのが印象的でした。

このときに私が感じたのは、データ構造の選択は「コードの書きやすさ」を超えて「業務の発想を広げる」ということです。Excel の表は便利ですが、「複数の表をどう組み合わせるか」を考えるときに、頭の中での操作がしにくくなります。Python のデータ構造(辞書のリスト・リストの辞書)に乗せ替えると、データを取り扱う発想の選択肢が一気に広がります。本コースでデータ構造を 4 つ並べたのは、皆さんに「業務データを Python の中でどう表現するか」の選択肢を持ち帰ってほしいからです。

まとめ

このレッスンでは、以下のことを学びました。

  • リスト(list):[ ] で書く順序付きの集まり。appendinsertremovepop で変更でき、lensumsortedin で使いやすく扱える
  • タプル(tuple):( ) で書く「変更できないリスト」。座標・RGB・関数の複数戻り値など、不変であるべき値に向く
  • 辞書(dict):{キー: 値} の組を集めたデータ構造。[キー] または .get() で取り出し、.items().keys().values()for に回す
  • 集合(set):重複しない値の集まり。和・差・積などの集合演算、重複削除、含まれるかの高速判定に使う
  • リスト内包表記:[式 for 変数 in 集まり] の形で、for ループを 1 行で書ける Python の便利な書き方
  • ネストしたデータ構造:辞書のリスト(複数件のレコード)、リストの辞書(カテゴリごとの集まり)が業務で頻出
  • 使い分けの 3 問:「順序が大事か」「変更するか」「キーで引きたいか」

次のレッスンでは、関数とモジュールを扱います。def での関数定義、引数と戻り値、スコープ、import での標準ライブラリの活用——コードに名前を付け、組み合わせる発想を身につけます。


確認クイズ

このレッスンの理解度をチェックしましょう。