はじめに
こんにちは!よわよわエンジニアの shungiku です。
本稿では、超よわよわだった当時の自分に向けて、「Pythonの辞書(ディクショナリ)型って結局何に使うの?使い所は?」といったことを解説していこうと思います。
なお、Pythonでは「辞書型」、「ディクショナリ型」という2通りの言い方がありますが、本稿では「辞書型」に統一して説明を進めます。
ディクショナリって何回も打ってるとタイポしそうなのでw(ヨコモジムズカシイ)
※ 辞書型の基本的な使い方については解説しないので、他の記事をご参照ください
辞書型の使い所がよく分からなかった
Pythonを学びたてだった頃の私は、ある日ついに辞書型との初対面を果たします。
「辞書!」という名前に引っ張られて、「きっと百科事典みたいなアプリを実装する時にでも使うんやろな〜。そんなもの作る予定ないから自分にはいらん知識かも」とか思いながら学習し始めます。
...
「どうやら辞書型とは以下のように波括弧で括って定義するデータ型らしい、、、ふむふむ。」
dic = {
'apple' : 5,
'orange' : 6
}
「こんな風にすると要素を追加したり変更したりできるらしい、、、ふむふむ。」
# 追加
dic['berry'] = 7
# 変更
dic['apple'] = 10
# print(dic)
# {'apple': 10, 'orange': 6, 'berry': 7}
「なるほどね〜、完全に理解した...!
...んで、これっていつ使うんだ?
しかも、このサンプルコードを見る限り "辞書っぽさ" は微塵も感じないんだが?!」
辞書型の使い所とは?
結論
データを構造化したい時に使う!
例えば、学生の成績管理アプリにおいて、学生を表現するためのデータ構造として、「名前」「年齢」「得点」をひとまとめにして
{
'name': 'Ken',
'age': 17,
'score': 80
}
こんな風に辞書型として取り回すと、例えば以下のような感じで値にアクセスできるようになって、コードの可読性がグッと上がるし、コーディングする時にもいろいろと楽になります!
student = {
'name': 'Ken',
'age': 17,
'score': 80
}
print(student["name"]) # student の name という感じで直感的に値にアクセスできる
# Ken
print(student["score"])
# 80
辞書型という名前に引っ張られて、百科事典アプリを作る時にしか使わないなんて思っちゃ駄目だぞ〜(← お前だけや)
留意事項
本稿では、「データを構造化してリストの要素として扱う」という用途を説明しますが、データの構造化以外にも用途はいくつかあります。
@shiracamus さんがコメント欄にて「値の変換」や「処理の振り分け」といった用途についてサンプルコード付きで説明してくださっているので、是非そちらもチェックを...!
成績アプリで考える具体例
とある試験を受けた学生達の平均点と平均年齢を算出するアプリを書いてみましょう。
(アプリというかスクリプトレベルの簡易なものですが)
まずは辞書型を使わずに書いてみる
以下のような CSV ファイルがあることを想定します。
name, age, score
Ken, 18, 70
Taro, 16, 90
Yumi, 17, 80
Kana, 16, 60
これらの学生の平均点を算出するスクリプトを書いてみましょう。
まずは、この CSV ファイルを読み込む関数を定義します。
本稿の本質ではないので、さらっと読み飛ばしていただいて OK です。
def load_score_csv(csv_path):
students = []
with open(csv_path) as f:
for index, line in enumerate(f):
# 先頭(index == 0)は 'name, age, score' なので無視してスキップ(continue)
if index == 0:
continue
# カンマ区切りで分割してリストにする
# 例) 'Ken, 18, 70\n' → (rstrip) → 'Ken, 18, 70' → (replace) → 'Ken,18,70' → (split) → ['Ken','18','70']
student = line.rstrip().replace(" ", "").split(",")
# 数値は str 型から int 型に変換しておく
student[1] = int(student[1])
student[2] = int(student[2])
students.append(student)
return students
この関数を使うと以下のように値が返ってきます。
(※ print出力は見やすいようにインデントしています)
students = load_score_csv("/path/to/file.csv")
print(students)
# [
# ['Ken', 18, 70],
# ['Taro', 16, 90],
# ['Yumi', 17, 80],
# ['Kana', 16, 60]
# ]
さて、これを元に学生の平均点と平均年齢を算出してみましょう。
total_score = 0
total_age = 0
for student in students:
total_age += student[1]
total_score += student[2]
average_age = total_age / len(students)
average_score = total_score / len(students)
print("平均年齢: ", average_age)
print("平均点: ", average_score)
# 平均年齢: 16.75
# 平均点: 75.0
できました!
...しかし、この3ヶ月後にこのコードを見直したあなたはきっとこう思うことでしょう。
total_age += student[1]
total_score += student[2]
「student[1]
とか student[2]
とか、、、これなんだっけ...?」
幸いにもこのサンプルコードの場合は、代入先の変数名にage
とか score
とかが付いているので、そこまで混乱することはないかもですが、必ずしもそんなケースばかりではありません。
また、今回のサンプルコードはとてもシンプルなので student[0]
みたいな表記でも脳ミソがオーバーフローすることはありませんが、プログラムの規模が大きくなり、そこら中に student[0]
や student[1]
が散在していると、「あれ?[0]
って年齢だっけ?得点だっけ?」と脳ミソがおかしくなってきます。
さらに今回は「名前」「年齢」「得点」だけのデータでしたが、さらに「性別」「出身地」「学生ID」などの情報が増えてくると、student[0]
, student[1]
, ..., student[4]
, ... といった具合にインデックス祭りになって発狂することになります。
辞書型を使って書いてみる
先程作った load_score_csv
関数を見直してみましょう。
この関数が返す値は以下のようなリストでした。
[
['Ken', 18, 70],
['Taro', 16, 90],
['Yumi', 17, 80],
['Kana', 16, 60]
]
要素一つ一つに着目すると以下のようなリストで学生が表現されています。
['Ken', 18, 70]
これを以下のデータ構造として表現するという方針で進めていきます。
そこで辞書型の出番という訳です。
{
'name': 'Ken',
'age': 18,
'score': 70
}
では、load_score_csv
関数を改修しましょう。
def load_score_csv(csv_path):
students = []
with open(csv_path) as f:
for index, line in enumerate(f):
# 先頭(index == 0)は 'name, age, score' なので無視してスキップ(continue)
if index == 0:
continue
# カンマ区切りで分割してリストにする
# 例) 'Ken, 18, 70\n' => (rstrip) => 'Ken, 18, 70' => (replace) => 'Ken,18,70' => (split) => ['Ken','18','70']
student = line.rstrip().replace(" ", "").split(",")
- # 数値は str 型から int 型に変換しておく
- student[1] = int(student[1])
- student[2] = int(student[2])
-
- students.append(student)
+ # 辞書型として学生を表現する
+ student_dict = {
+ "name": student[0],
+ "age": int(student[1]),
+ "score": int(student[2]),
+ }
+
+ students.append(student_dict)
return students
この関数が返す値をprint
すると以下のようになります。
(※ print出力は見やすいようにインデントしています)
[
{
'name': 'Ken',
'age': 18,
'score': 70
},
{
'name': 'Taro',
'age': 16,
'score': 90
},
{
'name': 'Yumi',
'age': 17,
'score': 80
},
{
'name': 'Kana',
'age': 16,
'score': 60
}
]
これにより、学生の平均点と平均年齢の算出部分は以下のように改修できます。
total_score = 0
total_age = 0
for student in students:
- total_age += student[1]
- total_score += student[2]
+ total_age += student["age"]
+ total_score += student["score"]
average_age = total_age / len(students)
average_score = total_score / len(students)
print("平均年齢: ", average_age)
print("平均点: ", average_score)
従来 student[2]
のように値にアクセスしていた箇所が、辞書型を使ってデータを構造化したおかげでstudent["score"]
のように直感的にアクセスできるようになりました。
可読性がグッと上がりましたね!
「年齢」や「得点」の他に、「性別」「出身地」「学生ID」といった具合に取り扱いたい情報が増えた場合にも、それぞれ適切に key を付与してデータ構造を定義してあげれば、可読性が悪化する心配もありません!
初学者へのワンポイント
自分が超よわよわだった頃、「辞書型の値を要素として持つリスト」という発想がそもそもありませんでした。
リストといえば、
[1, 10, 100]
とか
["a", "b", "c"]
のように、数値や文字列の値を要素として持つもの、というイメージしかなかった訳です。
しかし、本稿の具体例で出てきたように、辞書型だってリストに突っ込める訳です。
[
{
"user_id": "hoge",
"email": "hogehoge@example.com"
},
{
"user_id": "fuga",
"email": "fugafuga@example.com"
},
]
こういうのが頭にあるだけで、コーディングスキルの伸び具合がかなり違ってくると思います!
(さらに言うと、クラスだって当然リストに突っ込める訳なので、クラスも勉強済みだぜ!って方は是非是非試してみてください)
まとめ
- 辞書型はデータを構造化したい時に使える
- データが構造化されていると、コードの可読性がグッと上がる
- リストには数値や文字列だけではなく、辞書型だって突っ込める